개발

[제네릭] <T extends A> 와 <? extends A> 의 차이점은 무엇일까?

오렌지색 귤 2022. 3. 6. 10:28
반응형

갑자기 문득 이런 궁금증이 들었다.

 

어떤 메서드에서 파라미터로 받는 타입이 위의 두가지 경우일 때, 대체 뭐가 다를까?

 

어떤 경우에는 <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

 

 

What is difference between <? extends Object> and <E extends Object>?

What is difference between <? extends Object> and <E extends Object> ? When should one be used over the other?

stackoverflow.com

 

 

Why can't you have multiple interfaces in a bounded wildcard generic?

I know there's all sorts of counter-intuitive properties of Java's generic types. Here's one in particular that I don't understand, and which I'm hoping someone can explain to me. When specifying a...

stackoverflow.com

 

 

Java SE Specifications

Java Language and Virtual Machine Specifications Java SE 17 Released September 2021 as JSR 392 The Java Language Specification, Java SE 17 Edition HTML | PDF Preview feature: Pattern Matching for switch The Java Virtual Machine Specification, Java SE 17 Ed

docs.oracle.com

 

 

WildcardType (Java Platform SE 7 )

Returns an array of Type objects representing the lower bound(s) of this type variable. Note that if no lower bound is explicitly declared, the lower bound is the type of null. In this case, a zero length array is returned. For each lower bound B : if B is

docs.oracle.com

 

 

Why super keyword in generics is not allowed at class level

In Generics class A<T extends Number> is allowed But class A<T super Integer> is not allowed I'm not getting this point. This may sound like novice question but I'm stuck in it

stackoverflow.com

 

반응형