List<Type1>
은 List<Type2>
의 하위 타입도 상위 타입도 아니다.List<String>
은 List<Object>
의 하위 타입이 아니다.public class Stack {
public void pushAll(Iterable<E> src) {
for (E e : src) {
push(e);
}
}
}
Stack<Number>
로 선언한 후 pushAll(intVal)을 호출하면(여기서 intVal은 Integer 타입이다) 논리적으로는 잘 동작해야 할 것 같지만 그렇지 않다.public class Stack {
public void pushAll(Iterable<? extends E> src) {
for (E e : src) {
push(e);
}
}
}
Iterable<? extends E>
가 정확히 이런 뜻을 의미한다. 여기서 하위 타입은 자기 자신도 포함한다.public class Stack {
public void popAll(Collection<E> dst) {
while (!isEmpty()) {
dst.add(pop());
}
}
}
Collection<Object>
Collection<Number>
의 하위 타입이 아니다. 라는 오류가 발생한다(앞의 예시와 비슷하다).public class Stack {
public void popAll(Collection<? super E> dst) {
while (!isEmpty()) {
dst.add(pop());
}
}
}
<? extends T>
를 사용하고, 소비자라면 <? super T>
를 사용하도록 한다.겟풋 원칙(Get and Put Principle)
으로 불리기도 한다.public class Item31 {
public Chooser(Collection<? extends T> choices);
}
Chooser<Number>
의 생성자에 List<Integer>
를 넘길 수 있다.public class Item31 {
public static <E> Set<E> union(Set<E> s1, Set<E> s2);
}
public class Item31 {
public static <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2);
}
Set<E>
인 것에 주의해야 한다.public class Item31 {
void add(int value) {
// ...
}
add(10);
class Set<T> {
// ...
}
Set<Integer> = // ...;
}
public class Item31 {
public static <E extends Comparable<E>> E max(List<E> list);
public static <E extends Comparable<? super E>> E max(List<? extends E> list); // 와일드카드를 이용해서 다듬은 메서드
}
Comparable<E>
는 E 인스턴스를 소비한 뒤, 선후 관계를 뜻하는 정수를 생산한다.Comparable<E>
보다는 Comparable<? super E>
를 사용하는 편이 낫다.Comparable(혹은 Comparator)을 직접 구현하지 않고, 직접 구현한 다른 타입을 확장한 타입을 지원하기 위해서는 와일드카드가 필요하다.
List<ScheduledFuture<?>> scheduledFutures = ...;
를 처리할 수 없다.List<ScheduledFuture<?>>
를 거부한다. 아래의 상속 관계와 함께 생각해보자.public interface Comparable<E>;
public interface Delayed extends Comparable<Delayed>;
public interface ScheduledFuture<V> extends Delayedm Future<V>;
public class Item31 {
public static <E> void swap(List<E> list, int i, int j);
public static void swap(List<?> list, int i, int j);
}
List<?>
라는 타입의 리스트에는 null 외에는 어떤 값도 넣을 수 없다. 이 경우, 도우미 메서드를 따로 작성하여 활용하는 방법으로 사용할 수 있다. 이때, 실제 타입을 알아내려면 이 도우미 메서드는 제네릭이어야 한다.public class Item31 {
public static <E> void swap(List<E> list, int i, int j) {
swapHelper(list, i, j);
}
private static <E> void swapHelper(List<E> list, int i, int j) {
list.set(i, list.set(j, list.get(i)));
}
}
List<E>
임을 알고 있다.