Type Erasure(타입 소거)
자바에서 제네릭 타입에 사용된 타입 정보를 컴파일 타임에만 사용하고 런타임에는 소거하여 런타임 오버헤드 발생을 줄이는 방식을 말한다
이게 무슨 말인지 잘 이해가 가지 않아 정리를 하면서 이해해 보는 시간을 가졌다.
Type Erasure 규칙
- 모든 타입 파라미터를 그들의 바운드나 Object 타입으로 교체한다.
- 제네릭 타입을 제거한 후 타입이 일치하지 않으면 타입 캐스팅을 추가한다
- 확장된 제네릭 타입의 다형성을 보존하기 위해 브릿지 메서드를 생성한다.
각 규칙이 어떻게 적용되는 것인지 살펴보도록 한다
파라미터 타입을 바운디드와 언바운디드 타입으로 제네릭 클래스를 선언해 바운드 타입, Object 타입으로 컴파일 후 변환이 되는지 확인
바이트 코드로 변환한 결과를 보면 Java/Lang/Object, Main1/Node로 언바운디드 타입으로 선언한 data는 Object로, Node라는 바운디드 타입은 Node로 교체되었다.
`T`의 언바운디드 타입을 바운디드 타입으로 교체하게 되면 Object가 아닌 바운디드 타입으로 교체된다
이러한 과정은 제네릭 메서드에서도 똑같이 일어난다고 한다
만약 위 메서드를 count(Integer[] anArray, Integer elem) 같은 형태로 호출하게 되면 두 번째 규칙에 따라 Object를 Integer로 캐스팅한다고 생각하면 된다
제네릭 타입의 클래스나 인터페이스를 상속하거나 구현할 때 컴파일러는 필요에 따라 브릿지 메서드를 생성한다
public static class Node<T> {
public T data;
public Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public void setData(T data) {
this.data = data;
}
}
public class MyNode extends Node<Integer> {
public MyNode(Integer data, Node<Integer> next) {
super(data, next);
}
@Override
public void setData(Integer data) {
super.setData(data);
}
}
Node라는 제네릭 클래스에 MyNode가 Node를 상속받은 상황에서 타입소거를 진행하면 다음과 같이 변할 것이다.
public static class Node<T> {
public Objcet data;
public Node<Objcet> next;
public Node(Objcet data, Node<Objcet> next) {
this.data = data;
this.next = next;
}
public void setData(Objcet data) {
this.data = data;
}
}
public class MyNode extends Node<Integer> {
public MyNode(Integer data, Node<Integer> next) {
super(data, next);
}
@Override
public void setData(Integer data) {
super.setData(data);
}
}
MyNode에서 setData의 파라미터 타입이 일치하지 않기 때문에 메서드를 오버라이드 하지 못하게 되는 것이며
이러한 문제를 해결하고 다형성을 보존하기 위해 세 번째 규칙에 따라 브릿지 메서드를 생성한다.
왜 이러한 작업들이 생겨났을까? 자바에서의 제네릭의 등장부터 생각해야 된다.
자바의 제네릭은 jdk 1.5부터 등장했고, 상위 버전과 하위 버전의 호환성 즉 하위 호환성을 맞추기 위해 제네릭이 도입되기 전의 코드도 정상적으로 작동시키기 위함
-> 제네릭을 지원하기 위해 런타임 시에 제네릭 정보를 지우는 Type erasure 방법이 도입된 것
위의 내용을 잘 알고 있다면 다음과 같은 상황에 대처할 방법을 고민해 볼 수 있을 것 같다.
제네릭을 파라미터로 받고, 타입에 따라 행하는 행위가 다를 때 같은 이름의 메서드로 정의를 해놓는 메서드 오버로딩을 생각하면서 비즈니스 로직을 구현했다면 아래와 같은 에러를 만날 것이다.
Type Erasure로 인해 List<Integer> 와 List<String>은 런타임에 타입 정보를 알 수 없어 same erasure로 처리되는 것이다
같은 이름의 메서드가 두 개가 있다고 판단. 이러한 타입을 실체화 불가능 타입(non-reifiable)이라고 한다
메서드의 이름을 바꾸어 사용하던가, 일급 컬렉션을 활용해 보는 등의 고민이 필요할 것으로 보인다
일급 컬렉션에 대해서도 정리를 해봐야겠다
Reference
https://docs.oracle.com/javase/tutorial/java/generics/erasure.html
https://www.baeldung.com/java-type-erasure
'Java' 카테고리의 다른 글
모던 자바 인 액션 - PART 2 (0) | 2023.08.20 |
---|---|
모던 자바 인 액션 - PART1 (0) | 2023.08.19 |
Item9 try-finally보다는 try-with-resources를 사용하라 (0) | 2023.02.02 |
Item8 finalizer와 cleaner 사용을 피하라 (0) | 2023.01.15 |
Item7 다 쓴 객체 참조를 해제하라 (1) | 2023.01.12 |