숏컷 코드
List<String> names = new ArrayList<>();
names.add("Kim");
String first = names.get(0);문법
어떤 제네릭 형태를 먼저 떠올리면 되나
| 상황 | 먼저 떠올릴 것 |
|---|---|
| 타입 안전한 컬렉션 | List<String> 같은 제네릭 타입 |
| 다양한 타입에 재사용 | 제네릭 클래스 |
| 반환 타입이 입력 타입과 연결됨 | 제네릭 메서드 |
| raw type 경고 제거 | 타입 파라미터 명시 |
제네릭이 없었을 때의 문제
제네릭이 없던 시절에는 컬렉션에서 값을 꺼낼 때 항상 Object로 받고 cast해야 했습니다. 잘못된 타입이 들어가도 런타임에서야 ClassCastException이 발생했습니다. 제네릭은 이런 오류를 컴파일 타임으로 끌어올립니다.
// ❌ raw type 사용 (제네릭 없음) — 런타임 오류 위험
List list = new ArrayList();
list.add("hello");
list.add(42); // 다른 타입도 들어감
String s = (String) list.get(1); // ClassCastException!
// ✅ 제네릭 사용 — 컴파일 타임에 오류 감지
List<String> list = new ArrayList<>();
list.add("hello");
list.add(42); // 컴파일 오류 — String이 아님타입 매개변수와 재사용
제네릭의 핵심은 코드 재사용보다 타입 관계 보존입니다. 같은 자료구조라도 어떤 타입을 담는지 정확히 표현할 수 있어 API가 안전하고 읽기 쉬워집니다.
// 제네릭 클래스: 어떤 타입의 쌍이든 담을 수 있음
public class Pair<A, B> {
private final A first;
private final B second;
public Pair(A first, B second) {
this.first = first;
this.second = second;
}
public A getFirst() { return first; }
public B getSecond() { return second; }
}
Pair<String, Integer> entry = new Pair<>("Alice", 42);
String name = entry.getFirst(); // cast 없이 String
Integer score = entry.getSecond(); // cast 없이 Integer
// 제네릭 메서드: 반환 타입이 입력 타입과 연결됨
public static <T> T firstOrNull(List<T> list) {
return list.isEmpty() ? null : list.get(0);
}
String first = firstOrNull(names); // cast 없이 String으로 반환
Integer num = firstOrNull(numbers);raw type을 피해야 하는 이유
raw type(List list)을 쓰면 컴파일러 경고가 나고 제네릭의 이점이 사라집니다. Java 제네릭은 타입 소거(type erasure) 방식으로 구현돼 있어 바이트코드에는 타입 정보가 지워지지만, 컴파일 타임 검사는 제네릭이 제공하는 핵심 가치이므로 raw type 사용은 피해야 합니다. wildcard와 bounded type은 다음 카드(제네릭 와일드카드와 bounded type)에서 다룹니다.
체크포인트
| 상황 | 적합한 선택 |
|---|---|
| 타입 안전한 컬렉션 | List<String>, Map<String, Integer> |
| 다양한 타입에 재사용 가능한 유틸 | 제네릭 메서드 <T> |
| cast 없이 값 꺼내기 | 제네릭 컬렉션 사용 |
| raw type 경고 | 반드시 타입 파라미터 명시 |
| 다음 단계 | wildcard (?), bounded type (<T extends Number>) |
주의할 점
raw type은 제네릭의 모든 이점을 없앱니다. 타입 파라미터를 항상 명시하세요.
// ❌ raw type — 컴파일 경고 + 런타임 ClassCastException 위험
List list = new ArrayList();
list.add("hello");
list.add(123);
String s = (String) list.get(1); // 런타임 오류!
// ✅ 제네릭 명시 — 컴파일 타임에 오류 잡음
List<String> list = new ArrayList<>();
list.add("hello");
// list.add(123); // 컴파일 오류 — String이 아님
String s = list.get(0); // cast 불필요참고 링크
2 sources