와일드카드 GET / SET 경계

List<? extends U>

결론

  • GET : 안전하게 꺼내려면 U 타입으로 받아야 한다.

  • SET : 어떠한 타입의 자료도 넣을 수 없음(null 만 삽입 가능)

  • 꺼낸 타입은 U / 저장은 NO

이유

  • 메서드의 매개변수 제네릭 타입이 <? extends Fruit> 라는 것은 Apple, Banana, Fruit 타입을 전달 받아 내부에서 다룰 수 있다는 점이다.

  • 그런데 왜 GET 을 왜 Fruit 로 받아야 하냐면,

    • 만약, 매개변수에 List<Banana> 타입으로 들어올 경우, Apple f2 에 형제 캐스팅이 불가하기 때문이다.

    • 때문에, 이러한 논리 오류로 와일드카드 최상위 범위인 Fruit 타입으로만 안전하게 꺼낼 수 있는 것이다.

class FruitBox {
    public static void method(List<? extends Fruit> item) {
        // 안전하게 꺼내려면 Fruit 타입으로만 받아야한다
        Fruit f1 = item.get(0);

        Apple f2 = (Apple) item.get(0); // ! 잠재적 ERROR
        Banana f3 = (Banana) item.get(0); // ! 잠재적 ERROR
    }
}

public class Main {
    public static void main(String[] args) {
        List<Banana> bananas = new ArrayList<>(
                Arrays.asList(new Banana(), new Banana(), new Banana())
        );
        FruitBox.method(bananas);
    }
}
  • 또한, SET 을 어떠한 타입도 불가능하냐면

    • 만일 매개변수에 List<Banana> 타입으로 들어올 경우 형제 객체인 new Apple() 저장이 불가능하기 때문이다.

    • 만일 매개변수에 List<Fruit> 타입으로 들어올 경우 문제는 없겠지만, 위의 논리 오류로 그냥 컴파일 에러로 처리된다.

    • 따라서 만일 매개변수에 값을 넣고 싶다면 무조건 super 와일드카드를 사용해야 한다.

List<? super U>

결론

  • GET : 안전하게 꺼내려면 Object 타입으로만 받아야 한다.

  • SET : U와 U의 자손 타입만 넣을 수 있다.(U의 상위 타입 불가능)

  • 꺼낸 타입은 Object / 저장은 U 와 그의 자손만

이유

  • 메서드의 매개변수의 제네릭 타입이 <? super Fruit> 라는 것은 Fruit, Food, Object 타입을 전달 받아 내부에서 다룰 수 있다는 것이다.

  • 그런데 왜 GET 을 Object 타입으로 받아야 되냐면,

    • 만일 매개변수에 List<Food> 타입이 들어올 경우 Fruit f3 에 캐스팅이 불가하기 때문이다.

    • 이러한 논리 오류로 와일드카드 최상위 범위인 Object 타입으로만 안전하게 꺼낼 수 있는 것이다.

  • 또한, SET 을 거꾸로 Fruit 와 그의 자손 타입만 올 수 있냐면,

    • 만일 매개변수에 List<Fruit> 타입으로 들어올 경우 new Food() 저장이 불가능하기 때문이다.

    • 따라서 어떠한 타입이 와도 업캐스팅이 가능 상한인 Fruit 타입으로만 제한된다.

List<?>

결론

  • GET : 안전하게 꺼내려면 Object 타입으로만 받아야 한다. -> super 의 특징

    • 비제한 와일드카드는 어떤 타입이 들어올지 모르기 때문에, 최상위 타입인 Object 로 받아야 한다.

  • SET : 어떠한 타입의 자료도 넣을 수 없음(null 만 삽입 가능) -> extends 의 특징

    • 넣어야 하는 타입이 정해지지 않았는데? 값을 넣을수가 있나?

  • 꺼낸 타입은 Object / 저장은 NO

이유

Last updated