Java 제네릭과 타입 소거 (Generic & Type Erasure)
요약
Java 제네릭(Generic)의 개념과 타입 소거(Type Erasure)를 알아봅니다. Collection<Integer>로 선언해도 런타임에는 Object로 처리되어 오버로딩 메서드 선택에 영향을 주는 원리를 이해하여 정보처리기사 실기 문제를 풀어봅니다.
제네릭과 타입 소거 핵심 정리
| 개념 | 설명 | 예시 |
|---|---|---|
| 제네릭 | 타입을 파라미터로 받는 클래스/메서드 | Collection<Integer> |
| 타입 소거 | 컴파일 후 제네릭 타입 정보가 제거됨 | T -> Object |
| 오버로딩 결정 시점 | 컴파일 타임에 매개변수 타입으로 결정 | 타입 소거 후의 타입 기준 |
| 핵심 결론 | 제네릭 타입은 런타임에 Object로 취급 | print(Object x) 가 호출됨 |
제네릭이란?
제네릭(Generic) 은 클래스나 메서드를 정의할 때 타입을 미리 정하지 않고, 사용할 때 지정하는 기능입니다.
쉽게 말하면, "어떤 타입이든 담을 수 있는 상자" 를 만드는 것입니다.
여기서 T는 타입 파라미터라고 부릅니다. 실제로 사용할 때 구체적인 타입을 지정합니다.
| 코드 | T의 타입 | value의 타입 |
|---|---|---|
Box<Integer> | Integer | Integer |
Box<String> | String | String |
Box<Double> | Double | Double |
타입 소거란?
타입 소거(Type Erasure) 는 Java의 제네릭이 컴파일 후에 타입 정보가 제거되는 것을 말합니다.
핵심: 컴파일 전 vs 컴파일 후
컴파일러가 제네릭 타입 T를 모두 Object로 바꿔버립니다. 이것이 타입 소거입니다.
| 시점 | T의 타입 | value의 타입 |
|---|---|---|
| 컴파일 전 (소스 코드) | Integer (지정한 타입) | Integer |
| 컴파일 후 (실행 코드) | 제거됨 | Object |
타입 소거와 오버로딩
이 개념이 시험에 출제되는 핵심입니다. 오버로딩과 타입 소거가 만나면 예상과 다른 결과가 나올 수 있습니다.
문제 상황
Printer 클래스에는 같은 이름의 print 메서드가 3개 있습니다. 매개변수 타입만 다릅니다.
오버로딩에서 배운 대로, Java는 전달하는 인자의 타입에 따라 어떤 메서드를 호출할지 결정합니다.
| 호출 코드 | 인자 타입 | 호출되는 메서드 |
|---|---|---|
print(new Integer(5)) | Integer | print(Integer x) -> "A5" |
print(new Object()) | Object | print(Object x) -> "B..." |
print(3.14) | Double (Number의 하위) | print(Number x) -> "C3.14" |
제네릭 클래스에서 호출하면?
new Collection<Integer>(0)으로 만들었으니, value는 Integer이고 print(Integer x)가 호출될 것 같습니다.
하지만 실제로는 print(Object x) 가 호출됩니다.
왜 print(Object x) 가 호출될까?
오버로딩의 메서드 결정은 컴파일 타임에 이루어집니다.
타입 소거로 T가 Object로 바뀌기 때문에, 컴파일러는 print(Object x) 메서드를 선택합니다.
실전 예제: 단계별 풀이
24년 3회 기출문제와 동일한 구조로 단계별 풀이를 연습합니다.
1단계: 클래스 구조 파악
| 클래스 | 역할 |
|---|---|
Printer | print 메서드가 3개 오버로딩 (Integer, Object, Number) |
Collection<T> | 제네릭 클래스, T 타입의 value를 저장 |
Gamja | main 메서드에서 실행 |
2단계: 객체 생성
Collection<Integer>이므로 T = Integer- 생성자에 0을 전달 -> value = 0 (int 0이 Integer로 오토박싱)
3단계: 타입 소거 적용 (핵심!)
컴파일 후 Collection 클래스는 다음과 같이 변환됩니다.
4단계: 오버로딩 메서드 선택
value의 컴파일 타임 타입이 Object이므로:
| 후보 메서드 | 매개변수 타입 | 일치 여부 |
|---|---|---|
print(Integer x) | Integer | Object != Integer |
print(Object x) | Object | Object == Object |
print(Number x) | Number | Object != Number |
print(Object x) 가 선택됩니다.
5단계: 출력
출력 결과: B0
전체 실행 흐름
자주 하는 실수
| 실수 | 올바른 이해 |
|---|---|
Collection<Integer>이니까 print(Integer x) 호출 | 타입 소거 후 T는 Object이므로 print(Object x) 호출 |
| 런타임에 value가 Integer이니까 Integer 메서드 호출 | 오버로딩은 컴파일 타임에 결정, 런타임 타입과 무관 |
| Number는 Integer의 부모이니까 print(Number x) 호출 | 컴파일 타임 타입이 Object이므로 Object만 매칭 |
문제 풀이 전략
풀이 순서
- 제네릭 클래스 확인:
<T>같은 타입 파라미터가 있는지 확인 - 타입 소거 적용: T를 Object로 바꿔서 생각하기
- 오버로딩 메서드 확인: 매개변수 타입으로 어떤 메서드가 호출되는지 판단
- 결과 도출: 선택된 메서드의 출력 확인
핵심 공식
이 흐름만 기억하면 제네릭 + 오버로딩 문제를 풀 수 있습니다.