Page 23 -
P. 23
class StringDataHolder(data: String) : DataHolder<String>(data)
// 타입 인자를 상위 타입의 타입 인자로 넘김
class TreeNode<T>(data: T) : DataHolder<T>(data) { ... }
컴파일러는 일반 생성자 호출과 달리 생성자 위임 호출의 타입 인자를 추론해주지 못한다. 따라서
항상 위임 호출의 타입 인자를 명시해야 한다. 이 두 가지 경우를 비교해보자.
// error: one type argument expected for class DataHolder<T>
class StringDataHolder(data: String) : DataHolder(data)
// Ok: DataHolder<String>을 컴파일러가 추론함
fun stringDataHolder(data: String) = DataHolder(data)
9
타입 파라미터를 상속하지 않는다는 사실에 유의하라. 생성자 파라미터를 상위 타입 생성자의 인 제네릭스
자로 전달하는 것과 비슷하게, 타입 파라미터를 상위 타입의 타입 인자로 전달해야 한다. 따라서
TreeNode의 T와 DataHolder의 T는 서로 다른 선언이다. 실제로 두 제네릭 타입 정의가 서로 다른
타입 파라미터 이름을 사용해도 문제가 없다.
class TreeNode<U>(data: U) : DataHolder<U>(data) { ... }
앞의 예제 코드에 있는 addChild()나 children 정의를 보면 알 수 있는 것처럼 제네릭 클래스에
정의된 함수와 프로퍼티에서 클래스의 타입 파라미터를 사용할 수 있다. 또한, 프로퍼티나 함수에
타입 파라미터를 추가하면 프로퍼티나 함수 자체를 제네릭으로 만들 수 있다.
fun <T> TreeNode<T>.addChildren(vararg data: T) {
data.forEach { addChild(it) }
}
fun <T> TreeNode<T>.walkDepthFirst(action: (T) -> Unit) {
children.forEach { it.walkDepthFirst(action) }
action(data)
}
val <T> TreeNode<T>.depth: Int
get() = (children.asSequence().map { it.depth }.maxOrNull() ?: 0) + 1
fun main() {
val root = TreeNode("Hello").apply {
addChildren("World", "!!!")
363
Kotlin_05.indd 363 2022-02-15 오후 4:08:04