Generic Type erasure
자바에서 Generic Type erasure 프로세스는 말 그대로 제네릭 타입을 지워버리는 프로세스다. 자바에서 제네릭은 런타임에 소거된다. 제네릭은 타입 검사 이점이 있으므르 컴파일때 타입 검사를 보장한다. 그러나 런타임에는 타입 정보를 삭제한다.
런타임에 타입을 삭제한다?
모든 타입을 Object 타입으로 바꾼다.
또는 명시된 바운드로 바꾼다.
즉 컴파일 / 런타임 때 아래와 같이 다르다.
// 컴파일시 타입 체크
public class Test<T> {
public void test(T test) {
...
}
}
// 타입 소거가 된 런타임
public class Test {
public void test(Object test) {
...
}
}
만약 bound 되었다면 한정된 타입으로 소거된다.
// 컴파일시 타입 체크
public class Test<T extends Comparable<T>> {
private T data;
public T get() {
return data;
}
}
// 타입 소거된 런타임
public class Test { // 클래스명 옆 제네릭 소실
private Comparable data; // T --> Comparable
public Comparable get() { // T --> Comparable
return data;
}
}
이런 것을 "제네릭 타입 소거" 라고 한다.
왜 런타임때 타입을 지워서 알 수 없게 만드는 걸까 궁금해진다.
제네릭은 jdk 1.5에 도입되었는데, 하위 버전 호환성을 위해 제네릭을 소거한다고 한다.
아하! 제네릭은 '컴파일때' 타입보장을 한다는 의미가 조금 더 생생해진다.
∨ 실체화 불가능 타입
제네릭처럼, 런타임에 타입 정보를 정확히 알 수 없는 타입을 '실체화 불가능 타입'이라고 한다.
List는 실체화 불가능 타입이다. 그래서 런타임에 JVM은 리스트 원소의 타입을 알지 못한다.
반면 실체화 가능 타입은, 원시타입, 논제네릭, List<?>처럼 null값만 담을 수 있는 unbound wildcard, 배열 등이 있다. 타입 정보를 JVM이 알고 있다!
∨ 제네릭에 원시 타입이 불가능한 이유
그 이유는 제네릭 타입 소거와 관련이 있다. 제네릭을 사용한다면 소거에 의해 타입을 Object로 교체해야 하는 매커니즘을 가지고 있는데 원시타입은 Object가 아니라서 불가능하다.
∨ 제네릭 배열이 불가능한 이유
제네릭 배열이란 List<String>[] strings = new List<>[]; 같은 배열이다.
왜 제내릭에 배열을 넣으면 안될까?
ClassCastException 가능성이 있어서다.
먼저 알아야 할 원인은, 배열은 공변, 제네릭은 불공변이기 때문이다.
배열은 공변이라 Animal animal = new Cat()이 가능하지만, 제네릭은 불공변이라 List<Animal> animals = new ArrayList<Cat>(); 이 컴파일 에러가 난다는 것이다.
ClassCastException이 일어나는 이유는 뭘까. 제네릭 타입 소거 규칙 중에 "제네릭 타입을 제거한 후, 타입이 일치하지 않으면 타입캐스팅을 추가한다"는 규칙이 있다. 만약에 List<String>[] arrs이라는 배열이 있다고 해보자. arrs[0]은 List<String> 타입일텐데, 런타임시 타입소거에 의해 List 타입이 된다. 아무튼 List 타입인 arrs[0]에서 get(0)을 통해 원소를 가져오면, 제네릭에 표기한 String이 아니라 Object이다.
이때 만약 String s = arrs[0].get(0) 을 통해서 Object를 String에 담으려고 한다면, 소거된 타입인 List에서 get()을 호출할 때, 소거 규칙에 의하여 컴파일러가 자동으로 캐스팅 코드를 추가하게 된다. 이때 Object가 실제로 String타입이 아니라면, ClassCastException이 터지게 된다.
(공변에 대한 참고: 공변이면 다형성의 이점을 살릴 수 있다. 제네릭은 타입안전의 강한 이점을 가지기 위해 기본적으로 불공변이지만, 와일드카드를 사용하면 충분히 공변인 컬렉션을 만들 수 있다.)
.
.
Ref.
[Type Erasure Deep Dive] https://wisdom-and-record.tistory.com/134
[Generic Type erasure란 무엇일까?] https://devlog-wjdrbs96.tistory.com/263
[Java 제네릭에 대하여 + TypeReference] https://velog.io/@happyjamy/Java-제네릭에-대하여-TypeReference
[Java에서 배열을 공변으로 만든 이유?] https://hwan33.tistory.com/24
'JAVA' 카테고리의 다른 글
[Java] ObjectMapper에 TypeReference를 사용할 때 (0) | 2024.08.22 |
---|---|
[Java] 시간낭비 방지 메모: HashXX를 보고 O(1)을 곧바로 떠올렸어야지...! (1) | 2023.12.06 |
[JDBC] DB Connection을 얻어서 query 실행 | DriverManager과 DataSource를 통하여 (+ Connection Pool 개념) (0) | 2023.01.11 |
[JAVA] Iterator의 remove() 이해하기 (0) | 2022.09.08 |
[Java] String 연결하기(더하기) 효율적인 방법 (0) | 2022.09.07 |