반응형
1️⃣ 제네릭은 유연하지만, 타입 제약은 단단하다
자바의 제네릭은 타입 안정성을 높이는 대신,
타입 불일치에 매우 예민함
List<Object> objList = new ArrayList<>();
List<String> strList = new ArrayList<>();
objList = strList; // ❌ 컴파일 에러
직관적으로는 "String은 Object의 하위 타입이니까 괜찮지 않나?" 싶지만,
List<String>은 List<Object>의 하위 타입이 아님
왜냐하면, List<Object>에 Integer를 추가할 수도 있기 때문
즉, 제네릭 타입은 "불변"
2️⃣ 이때 등장하는 것이 “한정적 와일드카드”
?은 와일드카드(wildcard)로
"어떤 타입이 올지 모른다"는 뜻
여기에 extends나 super를 붙이면 "한정(bound)"할 수 있음
| 표현 | 의미 | 예시 |
| ? extends T | T의 하위 타입 | List<? extends Number> |
| ? super T | T의 상위 타입 | List<? super Integer> |
| ? | 제한 없음 | List<?> |
3️⃣ 왜 한정적 와일드카드를 써야 할까?
다음 메서드를 보자
public static void copy(List<Object> dest, List<Object> src) {
for (Object o : src) dest.add(o);
}
겉보기엔 괜찮지만,
List<Integer>나 List<String>을 인자로 넘기면 컴파일 에러가 발생함
왜냐하면 List<Integer>는 List<Object>의 하위 타입이 아니기 때문
4️⃣ 와일드카드로 유연하게 바꾸기
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (T item : src) dest.add(item);
}
이제는 다음과 같이 쓸 수 있음
List<Integer> numbers = List.of(1, 2, 3);
List<Number> objects = new ArrayList<>();
copy(objects, numbers); // ✅ 정상 작동
핵심은 두 가지
- ? extends T: 읽기 전용(Producer) - 데이터를 "꺼내는" 쪽
- ? super T: 쓰기 전용(Consumer) - 데이터를 "넣는" 쪽
// 데이터를 꺼내는 리스트
List<? extends Number> src = List.of(1, 2.5, 3L);
Number n = src.get(0); // ✅ 읽기 가능
src.add(4); // ❌ 추가 불가 (안전하지 않음)
// 데이터를 넣는 리스트
List<? super Integer> dest = new ArrayList<Number>();
dest.add(10); // ✅ 가능
Object x = dest.get(0); // ❌ 타입 안정성 보장 안 됨
5️⃣ 예시 — Stack API
public class Stack<E> {
private List<E> elements = new ArrayList<>();
public void pushAll(Iterable<? extends E> src) { // 생산자
for (E e : src) push(e);
}
public void popAll(Collection<? super E> dst) { // 소비자
while (!isEmpty()) dst.add(pop());
}
}
다음과 같이 쓸 수 있음
Stack<Number> numberStack = new Stack<>();
Iterable<Integer> ints = List.of(1, 2, 3);
Collection<Object> objs = new ArrayList<>();
numberStack.pushAll(ints); // ✅ Integer → Number OK
numberStack.popAll(objs); // ✅ Number → Object OK
한정적 와일드카드 덕분에 Stack이 훨씬 유연하게 동작함
반응형
'Java & Spring Boot' 카테고리의 다른 글
| ⚙️ 이펙티브 자바 item 48. 스트림 병렬화는 주의해서 사용하라 (0) | 2025.10.22 |
|---|---|
| 🧩 이펙티브 자바 item 41. 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라 (0) | 2025.10.21 |
| 🧩 이펙티브 자바 item 24. 멤버 클래스는 되도록 static으로 만들라 (0) | 2025.10.16 |
| ⚙️ 이펙티브 자바 item 13. clone의 재정의는 주의해서 진행하라 (0) | 2025.10.15 |
| Java의 ExecutorService 스레드 풀 정복하기 (0) | 2025.03.30 |