얼마 전에 이런 코딩하면서 코드에 이런 빨간줄이 생기는 걸 보았다.
왜 제네릭으로 타입 캐스팅을 하려는데 안되는 것이었다. 에러 메시지를 확인해보니 아래처럼 보여졌다.
Cannot use 'P' as reified type parameter. Use a class instead.
P는 reified type parameter 로 사용할 수 없다는 것이다.reified
가 뭘까? 왜 reified type parameter 가 아니면 사용하지 못할까?
Generic 을 컴파일하면 생기는 일
구글링을 해보니 reified 는 런타임에서 인자의 타입에 대한 정보를 알고 싶을때 사용한다고 되어 있었다. 즉, Generic은 런타임에서 제네릭 타입을 알 수 없다는 의미이다.
왜 그럴까?
이를 이해하려면 제네릭이 컴파일되고 런타임에서 실행이 될때 무슨일이 일어나는지 이해해야한다.
다음의 예제로 설명해보도록 하겠다.
public static <E> boolean containsElement(E [] elements, E element){
for (E e : elements){
if(e.equals(element)){
return true;
}
}
return false;
}
위 코드는 제네릭을 인자로 받아 배열에서 특정 요소가 있는지 여부를 체크하는 함수이다. 이 코드에서 Generic E
는 현재 어떤 타입인지 정해지지 않았다. (이 함수가 실행될때까지 아무도 어떤 타입인지 모른다) 그렇다면 이 코드가 컴파일 되게 하려면 어떻게 해야할까?
바로 Type erasure 를 하면 된다.
Type erasure
Type erasure 란 모든 제네릭 타입 인자를 bounded type 또는 unbounded type 으로 바꿔는 것을 말한다. (사실 바꾸면서 부가적으로 하는 일들이 몇가지 더 있다)
위 코드를 Type erasure 해보면 다음과 같다.
public static boolean containsElement(Object [] elements, Object element){
for (Object e : elements){
if(e.equals(element)){
return true;
}
}
return false;
}
Generic type parameter E
가 Object
(최상위 객체 클래스) 로 바뀌는 것을 볼 수 있다. (아마도 Generic Type 이 사라졌기 때문에 타입 소거라 한게 아닌가 싶다)
reified 란?
만약 타입 소거로 인해 알수 없게 된 타입 인자의 타입에 대해 알고 싶다면 어떻게 해야할까?
직접 인자에 Class<T>
를 넘겨줘도 되지만 이러면 가독성이 떨어질 수도 있다. (이런 함수를 만들어야 될지도 모른다)
fun <T> doSomething(someValue: T, Class<T> type)
하지만 kotlin 에서는 reified
라는 키워드를 사용하면 Class
인자를 전달하지 않아도 런타임에서 어떤 타입인지 알 수 있게 된다. (아래 코드에서 inline
키워드를 사용하는 이유는 제네릭 타입 인자를 사용할 때 발생하는 오버헤드를 줄이기 위해 사용하는 키워드이다)
inline fun <reified T> doSomething(someValue: T)
마무리
처음에 아무 생각 없이 사용하던 제네릭이 reified
키워드를 통해 여러 일들이 이루어진다는 것을 알 수 있었다.