Page 28 -
P. 28
class Registry<T> where T : Named, T : Identified {
val items = ArrayList<T>()
}
where 절을 클래스 선언 본문 앞에 추가하고 바운드할 타입 목록을 표시한다.
제네릭스의 문법을 맛봤으니 지금부터는 런타임에 제네릭스의 표현을 어떻게 처리할지 살펴보자.
9.1.3 타입 소거와 구체화
앞 예제에서는 타입 파라미터를 사용해 제네릭 선언 안의 변수, 프로퍼티, 함수 타입을 지정하는
것을 살펴봤다. 하지만 타입 파라미터가 항상 실제 타입을 대신할 수 있는 것은 아니다. 예를 들어
다음 코드를 살펴보자.
fun <T>TreeNode<Any>.isInstanceOf(): Boolean =
// error: cannot check for instance of erased type: T
data is T && children.all{ it.isInstanceOf<T>() }
의도는 주어진 트리의 노드나 자식의 노드가 모두 지정한 타입 T를 만족하는지 검사하는 것이다.
하지만 컴파일러는 data is T라는 식에 오류를 표시한다. 오류가 발생하는 이유는 타입 소거 때
문이다.
자바에 익숙하다면 자바 제네릭스에도 비슷한 제약이 있다는 점을 떠올릴 것이다. 이런 제약은 자
바 제네릭스가 자바 5부터 도입됐기 때문이다. 따라서 새 버전 자바 컴파일러와 가상 머신은 기존
(자바 5 이전) 코드와의 하위 호환성을 위해 기존 타입 표현 방식을 유지해야 했다. 그 결과 JVM에
서 타입 인자에 대한 정보는 코드에서 지워지고(그래서 타입 소거라는 용어가 나왔다), 소스코드에
서 List<String>이나 List<Number>와 같은 타입은 JVM에서 List라는 동일한 타입으로 합쳐진다.
코틀린은 1.0 버전부터 제네릭스가 있었지만, JVM이 주요 플랫폼이었기 때문에 자바와 같은 타
입 소거 문제가 생겼다. 런타임에 제네릭 코드는 파라미터 타입의 차이를 인식할 수 없고, 앞에서
본 data is T와 같은 검사는 기본적으로 아무 의미도 없다. isInstance() 함수가 런타임에 호출될
때 T가 어떤 타입을 뜻할지 알 방법이 없다. 마찬가지 이유로 제네릭 타입에 대해 is 연산자를 적
용하는 것도 의미가 없다. 다만 이런 경우에는 컴파일러가 타입 인자와 타입 파라미터가 서로 일
치하는지를 살펴보고, 그에 따라 경고나 오류를 보고한다.
368
Kotlin_05.indd 368 2022-02-15 오후 4:08:04