Dev Study/Java

[Java] 제네릭의 역설: 유연할수록 아무것도 담을 수 없다

parkhh98 2026. 1. 28. 18:57

Java 제네릭: "이 바구니에는 사과를 담을 수 없습니다"


1. 들어가며

자바 제네릭을 공부하다 보면 가장 당혹스러운 순간이 찾아옵니다. "분명히 과일 바구니라고 선언했는데, 왜 사과를 못 담게 하지?"
이 글은 ? extends T라는 외계어 같은 문법 뒤에 숨겨진, 자바 컴파일러의 "안전 과민증"에 대한 이야기입니다.

2. 환경

  • IDE: VS Code
  • JDK: Java 17

Phase 1. 상식의 배신 (왜 사과 바구니는 과일 바구니가 아닐까?)

현실 세계에서 "사과는 과일이다"는 참입니다. 그래서 친구가 "과일 좀 담아올 바구니 줘"라고 했을 때, 집에 있던 "사과 바구니"를 줘도 아무 문제가 없습니다. 어쨌든 과일을 담을 수 있으니까요.

하지만 자바는 아주 고지식합니다. 자바에게 사과 바구니(List<Apple>)와 과일 바구니(List<Fruit>)는 완전히 다른 물건입니다.

List<Fruit> basket = new ArrayList<Apple>(); // ❌ 컴파일 에러!

왜 이렇게 깐깐하게 굴까요? 만약 이걸 허용해주면, 사과 바구니로 둔갑한 과일 바구니에 누군가 바나나를 슬쩍 넣을 수 있기 때문입니다. 자바는 이런 오염(Pollution)을 끔찍하게 싫어해서, 아예 처음부터 "타입이 정확히 일치하지 않으면 연결하지 마!"라고 막아버립니다.

Phase 2. 타협점: "내용물은 묻지 않을게" (와일드카드)

개발자들이 불만을 터뜨립니다. "아니, 뭘 넣으려는 게 아니라 그냥 꺼내서 보기만 할 건데도 안 받아줘요?"
그래서 나온 타협책이 바로 와일드카드(?)입니다.

// "이 안에 뭐가 들었는진 정확히 모르지만, 적어도 Fruit의 일종인 건 확실해."
List<? extends Fruit> unknownBasket = new ArrayList<Apple>(); // ✅ OK!

이제 Apple 바구니든 Banana 바구니든 다 연결할 수 있습니다. 마치 바구니 겉면에 "과일(Fruit)류 포함됨"이라는 스티커를 붙인 것과 같습니다.

Phase 3. 대가: "대신 아무것도 못 넣습니다" (Read Only)

하지만 이 스티커(? extends Fruit)를 붙이는 순간, 자바는 아주 강력한 제약을 겁니다. 바로 "쓰기 금지(Write Ban)"입니다.

🧐 딜레마 상황
1. 지금 unknownBasket사과 바구니일 수도, 바나나 바구니일 수도 있습니다.
2. 당신이 여기에 Apple을 넣으라고 명령합니다.
3. 하지만 만약 실제 연결된 게 바나나 바구니라면? 바나나 바구니에 사과가 들어가는 사고가 터집니다.
4. 컴파일러: "실체가 뭔지 확신할 수 없으니(Unknown), 그냥 아예 뚜껑을 닫아버리자(Read Only)."
List<? extends Fruit> basket = ...;

Fruit f = basket.get(0); // ✅ 꺼내는 건 OK (뭐가 나오든 과일일 테니까)
basket.add(new Apple()); // ❌ 넣는 건 절대 금지! (실체가 바나나 바구니면 어쩔래?)

와일드카드 사용을 포함한 다양한 시도- 와일드카드를 사용하지 않을 때와 add를 하려고 하면 에러가 난다.

 

위의 출력 결과는 뭘까?

결과는 아래 더보기에

더보기

Basket Class: java.util.ArrayList


빨간 밑줄이 그어지는 이 순간이 바로 자바가 "안전"을 위해 문을 걸어 잠그는 장면입니다.

이것이 바로 제네릭의 역설입니다. "더 많은 종류를 받아들이려(유연함) 했더니, 정작 데이터를 넣을 능력(쓰기)을 잃어버렸다."

4. 결론: 언제 써야 할까?

코드를 외우는 건 의미가 없습니다. 이 원칙 하나만 기억하면 됩니다.

소비(Consume)할 때는 와일드카드를 써라. 생산(Produce)할 때는 쓰지 마라.

 

데이터를 꺼내서 읽기만 하는(Read) 조회용 메서드나 라이브러리(addAll 같은)를 만들 때는 <? extends T>가 최고의 선택입니다. 자바의 표준 라이브러리도 이 원칙을 철저히 지키고 있습니다.

java내부 구조도 wildcard를 사용한다는 Antigravity 답변

 

 "다양한 타입을 받아들이겠다"는 관용이 필요한 곳에는 어김없이 와일드카드가 쓰입니다.

하지만 데이터를 변경하고 추가해야 하는(Write) 곳에는 절대 쓰면 안 됩니다.

Title: Java 제네릭: "이 바구니에는 사과를 담을 수 없습니다" URL: java-generics-wildcard-paradox Tags: Java, Generics, Wildcard, TypeErasure, Programming Description: 제네릭의 와일드카드(? extends T)가 왜 쓰기 작업을 막는지, 코드 암기가 아닌 '바구니 라벨' 비유를 통해 직관적으로 이해해 봅니다. Category: Resource/Java Status: Published