갑자기 문득 이런 궁금증이 들었다.
어떤 메서드에서 파라미터로 받는 타입이 위의 두가지 경우일 때, 대체 뭐가 다를까?
어떤 경우에는 <T extends A>
를 사용하고 어떤 경우에는 <? extends A>
를 사용해야 할까?
결론부터 말하자면..
<T extends A>
는 타입 T를 사용해야 하는 경우에 사용하고, <? extends A>
는 타입을 사용할 필요가 없을 때 사용한다.
그러나 이 대답이 완벽하지는 않다.
완벽한 대답을 알아보도록 하자
해답
우선 <? extends A>
는 bounded wildcard 라고 부르며, <T extends A>
는 type bounded 라고 부른다.
차이점 1. Multiple bounds는 Type parameter bounds 에서만 가능하다
예를 들어 T extends A & B
는 가능하지만, ? extends A & B
는 불가능하다
Java Language Specification
4.9 Intersection Types An intersection type takes the form T1 & ... & Tn, n>0, where Ti, 1in, are type expressions. Intersection types arise in the processes of capture conversion (§5.1.10) and type inference (§15.12.2.7). It is not possible to write an intersection type directly as part of a program; no syntax supports this. The values of an intersection type are those objects that are values of all of the types Ti, for 1in.
간단한 코드로 알아보면 쉽게 이해할 수 있다.
List<? extends A & B> list = new ArrayList<>();
... // 생략
list.get(0);
위의 코드에서 Multiple bounds가 bounded wildcard 에서 가능하다면 list.get(0)
의 반환 타입은 어떤 타입이어야 하는가?
A 타입이어야 하는가? B 타입이어야 하는가?
컴파일러는 알 수가 없다..
List<T extends A & B> list = new ArrayList<>();
... // 생략
list.get(0);
위의 코드에서는 런타임 시점에 들어오는 타입 파라미터 T로 반환 타입을 지정해줄 수 있게 된다.
좀 더 깊게
java.lang.reflect.WildcardType
인터페이스에는 Type[] getUpperBounds()
와 Type[] getLowerBounds()
라는 두 메서드가 존재한다.
그래서 WildcardType 인터페이스를 구현한 와일드카드 타입은 UpperBounds나 LowerBounds를 배열으로 저장할 수 있으며, 이 말은 즉슨 multiple bounds가 가능하다는 말과 다를 바가 없어보인다.
그런데 왜 불가능한가?
그 이유는 자바 언어를 만들 때 그런 제약을 만들었기 때문이다.
// one or many? Up to language spec; currently only one, but this API
// allows for generalization.
위의 comment가 소스 코드에 적혀있는데, WildcardType 인터페이스를 만든 개발자는 해당 언어 스펙에 따라서 여러개를 받을 수도 있겠지만, 우린 하나만 받을거야~ 라고 알려줍니다.
아마 여러 타입을 받게 되면 이를 해결하기 위해 복잡한 상황을 마주하게 될 것이라 설계를 이런식으로 한 것 같습니다.
차이점 2. Lower bounds는 wildcard 에서만 가능하다
예를 들어 ? super A
는 가능하지만, T super A
는 불가능하다
생각해보면 super bound가 class definition에서 허용되지 않는 것은 당연하다
class Car<T super HybridCar> { }
위의 코드에 해당하는 설계는 말이 되지 않는다.
위의 코드에서는 HybridCar 타입을 소거할 수 없게 되는데, 소거하는 순간 Car의 타입은 무조건 Object가 되기 때문이다.
결국 런타임에서는 Car 클래스 타입에 HybridCar의 상위 타입이 아닌 모든 타입이 허용되게 되는 문제가 발생한다.
차이점 3. 메서드에서 파라미터의 타입을 동일하게 제한하고 싶다면 wildcard를 사용할 수 없다
public <T extends Number> void merge(List<T> list1, List<T> list2) { }
PS
'개발' 카테고리의 다른 글
2. Apis - 요약 (0) | 2022.12.04 |
---|---|
1. Getting Started - 요약 (0) | 2022.12.04 |
[열거 타입] Enum 공부하다 생긴 의문점 2가지 (0) | 2022.03.02 |
[제네릭] 타입 안전 이종 컨테이너.. 이거 어디에 사용할까? (0) | 2022.02.21 |
[제네릭] 컬렉션이나 단일 원소 컨테이너에서 매개변수화 되는 대상은 무엇일까? (0) | 2022.02.20 |