제네릭 타입
타입을 파라미터로 받는 클래스, 인터페이스, 메소드를 말한다.
public interface List<E> extends Collection<E> { /* ... */ }
interface List<E> : Collection<E> { /* ... */ }
제네릭 타입 소거
제네릭은 타입 소거(type
erasure
)를 사용해 구현되는데 런타임 시점에 제네릭의 인스턴스에는 타입 파라미터의 정보가 사라진다는 뜻이다.
예를 들어 List<String>
객체를 만들고 그 안에 문자열을 넣어도 런타임시 이 객체를 오직 List
로 인식한다.
제네릭 동작 및 제약
제네릭은 컴파일 타입에만 제약을 가하며 런타임 시점에 바운드 타입(bound
type
, 특정 타입으로 제한한다는 의미)이 있는 경우에는 해당 바운드 타입으로 대체하고 없는 경우에는 Object
로 대체한다.
결국 제네릭은 런타임 시점에 타입 소거로 인해 타입 정보가 사라져서 런타임 시점에는 타입 파라미터를 검사할 수 없게 된다.
val list1: List<String> = listOf("a", "b")
val list2: List<Int> = listOf(1, 2)
- 컴파일러는 위 리스트들을 다른 타입으로 인식하지만, 런타임 시점에는 같은 타입의 객체로 인식한다.
List<String>
는 문자열,List<Int>
는 정수만 들어있다고 가정할 수 있는 이유는 컴파일러가 올바른 타입만 각 리스트에 넣도록 보장해주기 때문이다.
reified 키워드
코틀린에서는 인라인 함수의 특성을 이용하여 컴파일러는 타입 파라미터로 쓰인 구체적인 클래스를 참조하는 바이트 코드를 생성해 삽입하고 reified
를 사용한 타입 파라미터를 실체화할 수 있게 되어 기존 제네릭의 제약을 극복할 수 있게 된다.
inline fun <reified T> isValue(value: Any) = value is T
- 함수에
inline
키워드를 붙여 인라인 함수로 만들고 타입 파라미터에reified
키워드를 붙이게 되면 런타임 시점에value
의 타입이T
의 인스턴스인지 확인할 수 있게 된다. reified
타입 파라미터를 사용하는 모든 함수는 반드시 인라인 되어야 한다.