제네릭과 와일드카드의 차이
1. 제네릭 (Generics)
- 클래스나 메서드가 다룰 데이터 타입을 파라미터화해서, 컴파일 시점에 타입 안전성을 보장하는 문법임.
- 선언 시점에 타입을 확정지음 → 컴파일러가 타입 체크 가능.
예시
List<String> names = new ArrayList<>();
names.add("철수"); // OK
names.add(123); // 컴파일 에러
- List<String> 제네릭을 통해 "이 리스트는 문자열만 담는다" 라는 타입 정보를 컴파일러에 알려줌.
- 잘못된 타입(Integer)을 넣으면 컴파일 단계에서 막힘.
2. 와일드카드 (Wildcard)
- 제네릭 타입을 다룰 때, 정확한 타입을 지정하지 않고 유연하게 범위를 한정하고 싶을 때 사용함.
- ? 로 표현됨.
- 제네릭의 소비/생산(Producer/Consumer) 관점에서 자주 등장.
종류
- 비한정 와일드카드 <?>
- "어떤 타입이든 들어올 수 있음"
List<?> list = new ArrayList<String>(); list = new ArrayList<Integer>();
- 단, 안전하지 않으므로 요소 추가는 불가(null만 가능).
- 요소 조회만 가능.
- 상한 제한 와일드카드 <? extends T>
- "T 또는 T의 하위 타입만 올 수 있음"
- 읽기(Producer) 용도
List<? extends Number> list = new ArrayList<Integer>(); Number n = list.get(0); // OK // list.add(10); // 컴파일 에러 (추가 불가)
- 하한 제한 와일드카드 <? super T>
- "T 또는 T의 상위 타입만 올 수 있음"
- 쓰기(Consumer) 용도
List<? super Integer> list = new ArrayList<Number>(); list.add(10); // OK (Integer는 허용) // Integer i = list.get(0); // 불가능 (Object로만 꺼낼 수 있음)
3. 핵심 차이
구분 | 제네릭 | 와일드카드 |
선언 위치 | 클래스/인터페이스/메서드 정의 시 사용 | 이미 정의된 제네릭 타입을 사용할 때 유연성을 위해 사용 |
타입 확정 여부 | 구체적인 타입을 지정 (List<String>) | 타입을 알 수 없거나 범위를 지정 (List<?>, List<? extends Number>) |
목적 | 컴파일 시점 타입 안정성 확보 | 메서드 파라미터의 유연성 확보 |
활용 | 직접 사용하는 코드 | API 설계 시 (라이브러리, 범용 메서드) |
4. 직관적인 비유
- 제네릭은 "이 상자에는 사과만 담을 거야" 라고 처음부터 딱 정해버리는 것.
- 와일드카드는 "어떤 과일이 들어있을지 모르지만 과일 계열만 올 수 있어" 혹은 "과일 이상(슈퍼클래스)만 허용할게" 라고 범위를 열어두는 것.
5. 예시 비교
// 제네릭 방식
public <T> void printList(List<T> list) {
for (T elem : list) {
System.out.println(elem);
}
}
// 와일드카드 방식
public void printList(List<?> list) {
for (Object elem : list) {
System.out.println(elem);
}
}
- 제네릭은 T를 선언해서 내부적으로 타입을 유지할 수 있음.
- 와일드카드는 읽기 전용 느낌 → 타입 정보를 유지하지 않음.
6. 정리
- 제네릭: 타입 안정성 확보 (직접 코드 작성할 때 주로 사용)
- 와일드카드: 유연성 확보 (API 설계, 범용 라이브러리에서 자주 사용)
- PECS 원칙 (Producer Extends, Consumer Super)
- <? extends T> → 데이터를 꺼내(Produce) 읽기 전용
- <? super T> → 데이터를 집어넣기(Consume) 전용
출처 : ChatGPT
'BE > Java' 카테고리의 다른 글
변수 정리 (3) | 2025.07.22 |
---|---|
중첩, 내부클래스 (2) | 2025.07.21 |
LocalDate.parse(입력문자,포맷); (1) | 2025.05.29 |
date.format() (0) | 2025.05.28 |
DateTimeFormatter.ofPattern (0) | 2025.05.27 |