[Java] Type erasure 알아보기

이전 면접에서 type erasure에 대해 질문을 받았었는데 처음 듣는 개념이라 아무 말도 못 했기 때문에 오늘은 type erasure를 공부해 봤다. 

 

type erasure에 대해 알아보기 전에 제네릭의 개념을 알아야 하는데 간단하게 제네릭을 정의하면 클래스나 메서드를 선언할 때 <String>이나 <Integer> 같은 타입을 지정할 수 있는데, 이를 통해서 다양한 타입의 객체와 함께 쓰이면서, 유연하고 재사용 가능한 코드를 작성할 수 있다. 제네릭에 대해서는 이전에 정리한 글이 있어 참고하면 좋을 것 같다.

https://seungjjun.tistory.com/251

 

Type erasure


타입 소거(type erasure)란 컴파일 시 코드에서 제네릭 타입의 정보를 제거하고 Object 같은 기본 타입으로 대체하는 과정을 말한다. 

좀 더 간단히 말하면 제네릭 타입의 정보(<String>이나 <Integer> 같은)는 런타임 시점에 소거되어 사용할 수 없게 된다.

 

그럼 type erasure의 목적은 무엇일까?

 

타입 소거는 제네릭을 사용하지 않는 기존 코드와의 하위 호환성을 지원하는데 사용이 된다. 다시 말해 제네릭이 지원되지 않는 이전 버전에서 작성된 자바 코드와 상호 작용할 수 있게 도와주는 역할을 한다.

 

예시 코드를 보자

 

ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("hello");
String word = arrayList.get(0);

위 예시 코드는 컴파일 시점에 ArrayList의 String은 지워지고 Object 타입으로 대체가 된다.

 

ArrayList arrayList = new ArrayList<>();
arrayList.add("hello");
String word = (String) arrayList.get(0);
위 코드처럼 Object 타입으로 변경 되었기 때문에 list에서 get메서드로 꺼낼때 String으로 형변환(타입 캐스팅)이 이루어지는 과정이 추가된다.
 
즉, 런타임 시점에 제네릭 타입의 정보를 사용할 수 없기 때문에 런타임 시점에 제네릭 객체의 타입을 결정할 수 없다.
 

Type erasure의 한계점


 
type erasure의 주요 한계점 중 하나는 런타임 시점에 제네릭 타입 정보를 사용할 수 없다는 것이다. 즉, instanceof 연산자를 사용해서 객체가 특정 제네릭 타입인지 확인할 수 없다.
예시 코드로 확인해보자.
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("Hello");
if(arrayList instanceof List<String>) {
}
위 코드는 컴파일이 되지 않는 코드이다. 
type erasure때문에 instanceof 연산자를 제네릭 타입의 정보와 함께 사용할 수가 없다. 컴파일 시 제네릭 타입 정보가 소거되고 Object같은 기본 타입으로 대체가 되기 때문이다.
위와 같은 한계점을 해결하기 위해 자바의 와일드카드를 이용하여 해결할 수 있다.
위 코드 예시에서 와일드카드를 사용해 arrayList가 허용할 수 있는 타입의 범위를 지정하여 해결할 수 있다. 
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("Hello");
if(arrayList instanceof List<?>) {
    // ...
}
 
위 코드는 컴파일이 된 후 instanceof 연산자는 arrayList가 List의 인스턴스인지 확인하게 된다. 
 

정리

type erasure에 대해 정리하면 타입 소거(type erasure)는 제네릭 코드가 제네릭을 지원하지 않는 기존 코드와 하위 호환이 되도록 하는 기능이다.