공공부하자개발 · 영어 학습 노트
자바
고급모던 자바와 성능0/10 완료
  • 01제네릭과 와일드카드
  • 02멀티스레드와 동기화
  • 03람다식과 함수형 인터페이스
  • 04Stream API와 병렬 처리
  • 05Optional로 NPE 방지
  • 06메서드 활용 패턴 (고급)
  • 07Java 21 모던 문법
  • 08어노테이션·리플렉션·동적 프록시
  • 09CompletableFuture 심화와 가상 스레드 실전
  • 10JVM 메모리·GC·OOM 진단
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 고급 › 01 / 10

제네릭과 와일드카드

섹션 7진행 0 / 10
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6연습 문제7정리‹ 이전다음 ›

5. 자주 하는 실수 (Tip)

❌ 실수 1: raw type 사용

java
List orders = new ArrayList();      // raw type
orders.add(new Order());
Order o = (Order) orders.get(0);

raw type 은 제네릭 검사를 완전히 꺼버립니다. 경고(unchecked)가 나지만 컴파일은 되기 때문에 놓치기 쉽고, 다른 곳에서 엉뚱한 타입이 들어와도 get 하는 순간까지 아무도 모릅니다. raw type 이 남아 있는 이유는 오직 JDK 1.4 이전 코드와의 호환뿐입니다.

java
✅
List<Order> orders = new ArrayList<>();
orders.add(new Order());
Order o = orders.get(0);

❌ 실수 2: List<Dog> 를 List<Animal> 에 대입하려고 함

java
List<Dog> dogs = new ArrayList<>();
List<Animal> animals = dogs;        // 컴파일 에러: incompatible types

제네릭은 불공변입니다(2.6). 허용하면 animals.add(new Cat()) 으로 dogs 가 오염됩니다. "읽기만 할 것"이라면 와일드카드로 의도를 표현하세요.

java
✅
List<Dog> dogs = new ArrayList<>();
List<? extends Animal> animals = dogs;   // 읽기 전용 뷰
for (Animal a : animals) a.sound();

❌ 실수 3: ? extends 리스트에 add 시도

java
static void addDefaults(List<? extends Number> nums) {
    nums.add(0);        // 컴파일 에러: List<capture of ? extends Number>
}

? extends Number 는 "Number 의 어떤 하위 타입인지 모름"이므로 넣을 수 없습니다. 값을 넣는 파라미터(consumer)는 super 여야 합니다(PECS).

java
✅
static void addDefaults(List<? super Integer> nums) {
    nums.add(0);        // OK: Integer 의 상위 타입 리스트에는 Integer 를 넣을 수 있음
}

❌ 실수 4: 제네릭 배열 생성 / 가변인자 힙 오염

java
static <T> T[] toArray(T... items) {
    T[] copy = new T[items.length];    // 컴파일 에러: generic array creation
    return copy;
}

타입 소거 때문에 T[] 를 만들 수 없습니다. List<T> 를 반환하거나 Class<T> 토큰 또는 Arrays.copyOf 를 사용하세요.

java
✅
static <T> List<T> toList(T... items) { return new ArrayList<>(Arrays.asList(items)); }
// 또는
static <T> T[] copy(T[] src) { return Arrays.copyOf(src, src.length); }   // src 의 런타임 타입을 재사용

❌ 실수 5: static 필드에 클래스 타입 파라미터 사용

java
class Cache<T> {
    private static T lastValue;    // 컴파일 에러: non-static type variable T cannot be referenced from a static context
}

static 필드는 클래스당 하나인데, Cache<String> 과 Cache<Integer> 는 소거 후 같은 클래스입니다. 하나의 필드가 두 타입을 동시에 가질 수는 없습니다. static 메서드는 자기 자신의 타입 파라미터를 따로 선언하면 됩니다.

java
✅
class Cache<T> {
    private T lastValue;                           // 인스턴스 필드
    static <U> Cache<U> of(U initial) {           // 메서드 자체의 타입 파라미터 U
        Cache<U> c = new Cache<>(); c.lastValue = initial; return c;
    }
}

❌ 실수 6: 와일드카드를 반환 타입에 사용

java
static List<? extends Number> numbers() { ... }
List<? extends Number> ns = numbers();
ns.add(1);            // 호출자가 add 못 함 — 사용성 최악

와일드카드는 파라미터의 유연성을 위한 도구입니다. 반환 타입에 쓰면 호출자가 그 유연성의 대가(제약)를 떠안게 됩니다.

java
✅
static List<Number> numbers() { ... }    // 또는 <T extends Number> List<T>
자주 하는 실수 (Tip)
  • ❌ 실수 1: raw type 사용
  • ❌ 실수 2: List<Dog> 를 List<Animal> 에 대입하려고 함
  • ❌ 실수 3: ? extends 리스트에 add 시도
  • ❌ 실수 4: 제네릭 배열 생성 / 가변인자 힙 오염
  • ❌ 실수 5: static 필드에 클래스 타입 파라미터 사용
  • ❌ 실수 6: 와일드카드를 반환 타입에 사용
이전 섹션4 응용 변형 예제5 / 7다음 섹션6 연습 문제