제네릭은 자바 컴파일 단계에서만 사용되고, 컴파일 이후에는 제네릭 정보가 삭제됨
쉽게 이야기해서 컴파일 전인 .java에는 제네릭 타입 매개변수가 존재하지만, 컴파일 이후인 자바 바이트코드 .class에는 타입 매개변수가 존재하지 않는 것임
어떻게 변하게 되는지는 아래 코드를 통해 설명함 (100% 정확한 코드는 아니고 대략 이런 방식으로 작동한다고 이해하면 됨)
GenericBox.java
public class GenericBox<T> {
private T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
Main.java
Integer 타입 인자 전달void main() {
GenericBox<Integer> box = new GenericBox<Integer>();
box.set(10);
Integer result = box.get();
}
new GenericBox<Integer>()에 대해 아래와 같이 이해함public class GenericBox<Integer> {
private Integer value;
public void set(Integer value) {
this.value = value;
}
public Integer get() {
return value;
}
}
.class에 생성된 정보는 다음과 같음GenericBox.class
public class GenericBox {
private Object value;
public void set(Object value) {
this.value = value;
}
public Object get() {
return value;
}
}
T는 Object로 변환됨Main.class
void main() {
GenericBox box = new GenericBox();
box.set(10);
Integer result = (Integer) box.get(); //컴파일러가 캐스팅 추가
}
값을 반환 받는 부분을 Object로 받으면 안됨
자바 컴파일러는 제네릭에서 타입 인자로 지정한 Integer로 캐스팅하는 코드를 추가해줌
AnimalHospitalV3.java
public class AnimalHospitalV3<T extends Animal> {
private T animal;
public void set(T animal) {
this.animal = animal;
}
public void checkup() {
System.out.println("동물 이름: " + animal.getName());
System.out.println("동물 크기: " + animal.getSize());
animal.sound();
}
public T getBigger(T target) {
return animal.getSize() > target.getSize() ? animal : target;
}
}
//사용 코드 예시
AnimalHospitalV3<Dog> hospital = new AnimalHospitalV3<>();
...
Dog dog = animalHospitalV3.getBigger(new Dog());
AnimalHospitalV3.class
public class AnimalHospitalV3 {
private Animal animal;
public void set(Animal animal) {
this.animal = animal;
}
public void checkup() {
System.out.println("동물 이름: " + animal.getName());
System.out.println("동물 크기: " + animal.getSize());
animal.sound();
}
public Animal getBigger(Animal target) {
return animal.getSize() > target.getSize() ? animal : target;
}
}
T의 타입 정보가 제거되어도 상한으로 지정한 Animal 타입으로 대체되기 때문에 Animal 타입의 메서드를 사용하는데는 아무런 문제가 없음//사용 코드 예시
AnimalHospitalV3 hospital = new AnimalHospitalV3();
...
Dog dog = (Dog) animalHospitalV3.getBigger(new Dog());
Animal로 받으면 안되기 때문에 자바 컴파일러가 타입 인자로 지정한 Dob로 캐스팅하는 코드를 넣어줌자바의 제네릭은 단순하게 생각하면 개발자가 직접 캐스팅하는 코드를 컴파일러가 대신 처리해주는 것임
자바는 컴파일 시점에 제네릭을 사용한 코드에 문제가 없는지 완벽하게 검증하기 때문에 자바 컴파일러가 추가하는 다운 캐스팅에는 문제가 발생하지 않음
자바의 제네릭 타입은 컴파일 시점에만 존재하고, 런타임 시에는 제네릭 정보가 지워지는데 이것을 타입 이레이저라 함
.class로 자바를 실행하는 런타임에는 우리가 지정한 Box<Integer>, Box<String>의 타입 정보가 모두 제거됨class EraserBox<T> {
public boolean instanceCheck(Object param) {
return param instanceof T; // 오류
}
public void create() {
return new T(); // 오류
}
}
class EraserBox {
public boolean instanceCheck(Object param) {
return param instanceof Object; // 오류
}
public void create() {
return new Object(); // 오류
}
}
T는 런타임에 모두 Object가 되어버림instanceof는 항상 Object와 비교하게 됨Instanceof를 허용하지 않음new T는 항상 new Object가 되어버림new를 허용하지 않음