내가 아무 타입의 입력을 받고싶다고 마음대로 Object로 선언한다면?
=> 사용할 때 마다 다운 캐스팅을 해주어어야하는 번거로움이 발생한다
그래서 나온 것이 제네릭이다.
제네릭을 사용하면 타입의 안정성을 보장받을 수 있다
의도치 않은 타입의 객체가 들어오는 것을 컴파일 시점에 막아주고, 형변환 에러(ClassCastException) 없이 안전하게 사용할 수 있도록 보장하는 것
하지만, 제네릭의 작동 방식을 정확히 알고 사용해야한다.
제네릭 코드는 컴파일시에 타입이 삭제된다
컴파일 할 때 타입 조건을 정의하고 런타임에는 타입을 제거한다

이 때 사라지지 않는 타입을 Reifiable Type(실체화 타입)이라고 하는데
실체화 타입은
비실체화타입(Non reifiable type)은
컴파일 단계에서 타입소거에 의해 제거되는 타입을 말한다
제네릭 타입 파라미터들은 이 때 모두 소거된다
class Box<T extends Number> {
List<T> list = new ArrayList<>();
void add(T item) {
list.add(item);
}
T getValue(int i) {
return list.get(i);
}
}
위 코드를 보면서 타입 소거과정을 정리해보자
제네릭이 삭제되는 과정은 제네릭 클래스 안에 Erasure가 제거한다
T부분은 그럼 뭘로 바뀌는지 알아야한다
Box클래스는 Number로 치환된다. 만일 extends Number가 없다면 제네릭 타입은 모두 Object로 치환된다
class Box {
List list = new ArrayList(); // Object
void add(Number item) {
list.add(item);
}
Number getValue(int i) {
return list.get(i);
}
}
치환된 후에는 타입이 맞지않는 부분이 발생한다
여기서는 getValue가 타입이 맞지 않는다.
리스트의 get메서드는 Object타입을 반환하는데, 우리가 원하는 값은 Number 타입이니까, 형 변환이 필요한거지.
이 부분이 필요한 경우 컴파일러가 형 변환을 추가하는 것이다
cf)https://velog.io/@myhyeoeo/내일배움캠프-사전캠프-TIL-5일차
// 클래스의 제네릭 타입 T (예: Double로 지정했다고 가정)
class Box<T extends Number> {
// 제네릭 메서드: 메서드 앞에 <T>를 또 선언함!
// ★ 이때 메서드의 <T>는 클래스의 <T>와 이름만 같을 뿐 '완전히 다른 독립된 타입 매개변수'가 됩니다.
public <T> void printItem(T item) {
System.out.println(item);
}
}
위 코드를 보면 클래스와 독립적인 매개변수를 지닌다는 의미를 알 수 있다
클래스의 T는 Number로 치환되고, 메서드의 T는 Object로 치환된다
그럼,
Box<Double> box = new Box<>(); // 클래스의 T는 Double
box.printItem("Hello World"); // 메서드의 T는 String으로 추론됨!
그럼 위 클래스를 사용해서 box 인스턴스를 생성했을 때, Double로 생성했으니까, double만 다루는 클래스가 아닐까? 라는 생각이 들겠지만,
printItem 메서드는 double과 관계없는 String이 호출되었다
void printItem(Object item) 으로 치환되니까 가능하다
제네릭 메서드의 < T >는 클래스의 T를 덮어씌워 버리는 별개의 독립된 변수이기 때문에, 클래스에 지정한 타입 제한(Number 등)에 영향을 받지 않고 어떤 타입이 들어와도 형변환 에러 없이 독립적으로 동작한다는 의미입니다.
두 용어를 구분해서 잘 사용할 필요가 있다
작성 시점 (Source Code / Editing)
개발자가 .java 파일에 텍스트로 코드를 입력하고 저장하는 단계입니다.
컴파일 시점 (Compile Time)
작성한 자바 파일(.java)을 컴퓨터가 읽을 수 있는 바이트코드(.class 파일)로 번역(변환)하는 과정입니다.
javac Main.java 명령어를 실행하거나, IDE(IntelliJ 등)에서 Build/Rebuild를 누를 때 일어납니다.
주요 역할: 문법 에러 확인, 타입 검사, 제네릭 타입 소거(Type Erasure).
컴파일 과정은 내 눈에는 잘 보이지않는데, 코드를 짜다보면 붉은 줄로 밑줄이 그어진 곳을 발견할 수 있다. 이 때 컴파일 에러를 미리 확인할 수 있다.