Page 31 -
P. 31
fun main() {
val tree = TreeNode<Any>("abc").addChild("def").addChild(123)
println(tree.isInstanceOf<String>())
}
컴파일러는 isInstanceOf()를 인라인해서 T 대신 실제 타입인 String을 넣는다. 따라서 코드가 다
음과 같아진다.
fun main() {
val tree = TreeNode<Any>("abc").addChild("def").addChild(123)
println(tree.cancellableWalkDepthFirst { it is String })
}
9
자바에서 사용했던 접근 방식과 달리, 구체화한 타입 파라미터를 사용하는 해법은 안전하고(검
사하지 않는 캐스트를 쓰지 않음) 빠르다(코드가 인라인됨). 하지만 인라인 함수를 사용하면 컴 제네릭스
파일된 코드의 크기가 커지는 경향이 있다는 점을 조심해야 한다. 하지만 (앞의 코드에서 사용한
cancellableWalkDepthFirst()처럼) 코드에서 분량이 많은 부분을 별도의 인라인되지 않는 함수로
뽑아내면 코드 크기가 줄어드는 정도를 줄일 수 있다. 그리고 인라인 함수 안에서만 구체화한 타
입 파라미터를 쓸 수 있기 때문에 구체화한 타입을 클래스나 프로퍼티와 함께 쓸 수는 없다.
구체화한 타입 파라미터도 한계가 있다. 이로 인해 구체화한 타입과 완전한 타입이 서로 구분된다.
현재는 구체화한 타입 파라미터를 통해 생성자를 호출하거나 동반 객체 멤버에 접근할 수 없다.
// error: type parameter T cannot be called as function
inline fun <reified T> factory() = T()
그리고 구체화한 타입 파라미터를 구체화하지 않은 타입 파라미터로 대신할 수는 없다.
fun <T, U> TreeNode<*>.isInstanceOfBoth() =
isInstanceOf<T>() && sInstanceOf<U>()
이렇게 할 수 없는 이유 역시 타입 소거다. 이 코드에서 T나 U의 실제 타입을 알 수 없기 때문에 인
라인된 instanceOf() 함수를 안전하게 호출할 방법을 찾을 수 없다.
이상으로 코틀린 제네릭스에 대한 기본적인 설명을 마친다. 이제 더 고급 주제인 변성을 다룰 것
이다. 변성은 타입이 생산자와 소비자 중에서 어떤 역할을 하는지를 제어함으로써 더 유연하게 제
네릭 타입을 선언할 수 있게 해준다.
371
Kotlin_05.indd 371 2022-02-15 오후 4:08:04