섀도잉이란 무엇인가?
class Person(val name : String) {
fun addPerson(name : String) {
// ...
}
}
addPerson() 함수의 파라미터가 Person class의 프로터티 이름과 같아진다.
외부 스코프의 프로퍼티를 가리게 되는 경우를 섀도잉이라 한다.
이를 개발자가 알아차리기 힘든 경우가 생긴다.
타입 파라미터에서도 문제가 생긴다.
interface Payment
class KakaoPay: Payment
class NaverPay: Payment
class PaymentHistory<T: Payment> {
fun <T: Payment> addPayment(payment: T) {
// ...
}
}
이러한 경우는 PaymentHistory의 제네릭 타입과 addPayment()의 타입 파라미터가 독립적으로 적용된다.
val paymentHistory = PaymentHistory<KakaoPay>()
paymentHistory.addPayment(KakaoPay())
paymentHistory.addPayment(NaverPay()) // 말이 안 되면서도 말이 되는 경우..
정상적으로 동작한다.
문제가 되지 않기에, 개발자는 문제점을 쉽사리 알아차리기 쉽지 않다.
문제를 해결해보자
interface Payment
class KakaoPay: Payment
class NaverPay: Payment
class PaymentHistory<T: Payment> {
fun addPayment(payment: T) {
// ...
}
}
PaymentHistory Class의 제네릭 타입을 그대로 addPayment() 함수 파라미터에도 적용시켰다.
val paymentHistory = PaymentHistory<KakaoPay>()
paymentHistory.addPayment(KakaoPay())
paymentHistory.addPayment(NaverPay()) // 컴파일 에러 발생! Type mismatch
KakaoPay 타입만 받을 수 있게 클래스 제네릭을 그대로 함수 파라미터로 적용시켰기에, 오직 KakaoPay 타입만을 적용할 수 있다.
같은 인터페이스를 상속받았더라도 NaverPay 타입은 추가할 수가 없다.
클래스에 정의한 제네릭 타입과 별개로 사용하고 싶다면
interface Payment
class KakaoPay: Payment
class NaverPay: Payment
class PaymentHistory<T: Payment> {
fun <C: T> addPayment(payment: C) {
// ...
}
}
이런 식으로 의도적으로 함수에 제네릭을 명시하여 다른 타입도 적용할 수 있다.
근데 이럴 경우가 많이 있을지는 모르겠다.
반응형
'📚 Kotlin' 카테고리의 다른 글
[Effective Kotlin] 아이템 36. 상속보다는 컴포지션을 사용하라 (0) | 2023.01.30 |
---|---|
[Effective Kotlin] 아이템 32. 생성자 대신 팩토리 함수를 사용하라 (2) | 2023.01.29 |
[Effective kotlin] 아이템 27. 변화로부터 코드를 보호하려면 추상화를 사용하라 (0) | 2023.01.29 |
[Effective Kotlin] 아이템 24. 제네릭 타입과 variance 한정자를 활용하라 (1) | 2023.01.27 |
[감상문] Kotlin In Action을 읽고 (0) | 2023.01.17 |