본문 바로가기
Java

자바 중급 2편 - 제네릭(4)

by KongJiHoon 2025. 6. 1.

1. 와일드카드 (Wildcard)

(1)정의

와일드카드는 "?" 기호를 사용하여 불특정 타입을 다룰 수 있게 해준다. 특히 이미 생성된 제네릭 객체를 파라미터로 받을 때 유용하다.

✅ 핵심: 와일드카드는 제네릭을 정의할 때가 아니라, 사용할 때 사용

1) 기본 예시

class Box<T> {
    private T value;
    public void set(T value) { this.value = value; }
    public T get() { return value; }
}
Box<?> wildcardBox = new Box<String>(); // 모든 타입 수용 가능

 

 

2) 비제한 와일드카드 (?)

void print(Box<?> box) {
    System.out.println(box.get()); // 읽기 가능
    // box.set(...) → 불가!
}
  • 읽기만 가능, 쓰기는 불가능 (타입 불확정)
  • ? == <? extends Object>와 동일한 의미

3) 상한 와일드카드 (? extends T)

void print(Box<? extends Animal> box) {
    Animal a = box.get(); // OK
    // box.set(...) → 불가!
}
  • T 또는 T의 하위 타입만 받겠다
  • get()은 안전 (반환 타입이 상위 타입), set()은 불가

4) 하한 와일드카드 (? super T)

package com.example.generic.ex5;

import com.example.generic.animal.Animal;
import com.example.generic.animal.Cat;
import com.example.generic.animal.Dog;

public class WildcardMain2 {


    public static void main(String[] args) {
        Box<Object> objectBox = new Box<>();

        Box<Animal> animalBox = new Box<>();

        Box<Dog> dogBox = new Box<>();
        Box<Cat> catBox = new Box<>();


        // Animal 포함 상위 타입 전달 가능
        writeBox(objectBox);
        writeBox(animalBox);
        // Animal타입 이거나 상위타입만 가능
        // writeBox(dogBox); // 하한이 Animal


        Animal animal = animalBox.get();

        System.out.println("animal = " + animal.getName());
    }

    static void writeBox(Box<? super Animal> box) {
        box.set(new Dog("멍멍이", 100));
    }
}
  • T 또는 T의 상위 타입만 받겠다
  • set()은 가능, get()은 Object로 반환됨

5) PECS 원칙

  • Producer Extends → 데이터를 꺼낼 때는 extends
  • Consumer Super → 데이터를 넣을 때는 super

2. 제네릭 메서드

(1) 정의

public static <T> T genericMethod(T t) {
    return t;
}
  • 메서드 선언부에 <T>가 붙는 것이 핵심
  • 메서드 호출 시 타입이 결정됨

 

(2) 타입 제한

public static <T extends Number> T numberMethod(T t) {
    return t;
}
  • Number와 그 하위 타입만 허용 (Integer, Double 등)

(3) 타입 추론

Integer result = genericMethod(10); // 컴파일러가 Integer 추론
  • 명시적으로 <Integer>를 쓰지 않아도 컴파일러가 자동 추론

 

(4) 제네릭 메서드 vs 와일드카드

구분 제네릭 메서드 와일드카드
목적 타입을 변경해야 할 때 타입을 고정된 범위로 제한할 때
사용 시점 메서드 호출 시 타입 결정 이미 생성된 객체를 받을 때 사용
예시 <T> T copy(T t) void print(Box<? extends T>)

 

3. 타입 이레이저 (Type Erasure)

 

(1) 정의

  • 자바의 제네릭은 컴파일 타임 전용 기능
  • 컴파일 후 .class 파일에서는 모든 타입 정보가 제거됨

(2) 예시

GenericBox<Integer> box = new GenericBox<>();
box.set(10);
Integer result = box.get();

→ 컴파일 후:

GenericBox box = new GenericBox();
box.set(10);
Integer result = (Integer) box.get(); // 컴파일러가 캐스팅 코드 추가

 

예시 2)

 

컴파일전

public class AnimalHospitalV3<T extends Animal> {
    private T animal;

    public void set(T animal) {
        this.animal = animal;
    }

    public void checkup() {
        System.out.println("동물 이름: " + animal.getName());
        System.out.println("동물 크기: " + animal.getSize());
        animal.sound();
    }

    public T getBigger(T target) {
        return animal.getSize() > target.getSize() ? animal : target;
    }
}

 

 

컴파일 후

public class AnimalHospitalV3 {
    private Animal animal;

    public void set(Animal animal) {
        this.animal = animal;
    }

    public void checkup() {
        System.out.println("동물 이름: " + animal.getName());
        System.out.println("동물 크기: " + animal.getSize());
        animal.sound();
    }

    public Animal getBigger(Animal target) {
        return animal.getSize() > target.getSize() ? animal : target;
    }
}

 

  • T는 지워지고, 상한 타입인 Animal로 대체됨
  • 컴파일러가 T에 대해 Animal 타입처럼 사용해도 된다고 판단
  • 반환 시에는 호출부에서 타입 캐스팅을 자동 삽입

 

 

 

(3) 타입 매개변수 제한의 경우 – 제한 사항 (한계)

 

1. instanceof T 사용 불가

 

public class EraserBox<T> {
    public boolean isInstance(Object obj) {
        return obj instanceof T; // ❌ 컴파일 오류!
    }
}
  • 이유: 런타임에 T의 타입 정보가 사라짐 ⇒ T가 무엇인지 JVM은 모름
  • 대안: 클래스 타입을 매개변수로 전달받아 사용
 

 

 2. new T() 생성 불가

public class EraserBox<T> {
    public T create() {
        return new T(); // ❌ 컴파일 오류!
    }
}
  • 이유: 자바는 컴파일 시점에만 제네릭 타입을 알고, T의 생성자를 알 수 없음
  • 대안: 생성자를 인자로 받거나 리플렉션 사용
 

 

3. 제네릭 배열 생성 불가

T[] array = new T[10]; // ❌ 컴파일 오류!
  • 이유: 자바는 배열 생성 시 구체적인 타입이 필요하지만, T는 런타임에 존재하지 않음
  • 대안: Array.newInstance() 또는 Object 배열을 사용
  • 제네릭은 컴파일 시점까지만 존재하고, 런타임에는 모든 타입 정보가 제거됨 (Type Erasure).
    그 결과, 타입 매개변수에 대해 다음과 같은 제한이 존재한다.

 

 

 

정리

주제 요약
와일드카드 ?, ? extends, ? super로 표현. 객체를 읽을 때는 extends, 넣을 때는 super.
제네릭 메서드 메서드 호출 시 타입을 지정. 반환 타입/입력 타입이 일치할 때 사용.
타입 이레이저 컴파일 후 타입 정보가 지워짐. instanceof, new T() 불가능.

 

 

 

'Java' 카테고리의 다른 글

자바 중급 2편 - ArrayList(2)  (0) 2025.07.19
자바 중급 2편 - ArrayList(1)  (0) 2025.07.07
자바 중급 2편 - 제네릭(3)  (0) 2025.05.31
자바 중급 2편 - 제네릭(2)  (0) 2025.05.15
자바 중급 2편 - 제네릭(1)  (0) 2025.05.11