와일드 카드 (WildCards)
제네릭 코드에서 물음표 (?) 는 와일드 카드라고 불린다. 와일드 카드는 unkonwn type 을 의미한다.
와일드 카드는 매개변수의 타입, 필드 혹은 지역 변수와 리턴 타입등에 활용된다.
와일드 카드는 제네릭 메서드 호출 혹은 제네릭 클래스의 인스턴스화에는 사용될 수 없다.
Upper Bounded Wildcards
- 변수에 대한 제한을 완화 하고 싶은 경우 Upper Bounded Wildcards를 사용할 수 있습니다.
- 예를들어 List<Integer>, List<Doublce> and List<Number> 타입을 모두 받을 수 있는 메서드를 작성하고 싶은 경우 Upper Bounded Wildcards를 사용하면 됩니다.
Upper Bounded Wildcards를 선언하기 위해서는 wildcard character(’?’)에
extends
키워드를 달면 됩니다. 바운디드 타입 파라미터와 마찬가지로 이 문맥에서 extends 는 확장(extends)과 구현(implements)을 포괄합니다.public static void process(List
<? extends Foo>
list) { /* ... */ }
static void process(List<? extends Foo> list) { Foo foo = list.get(0); for (Foo elem : list) { // ... } }
foreach 문의 elem 변수는 Foo로 선언되며 Foo의 메서드를 활용할 수 있습니다.
Unbounded Wildcards
- unbounded wildcard 타입은 whildcard character(’?’)의 특별한 사용법입니다.
- 예를들어 List<?> 는 list of unkown type 이라고 부르며 이런 선언이 유용한 두가지 시나리오가 있습니다.
- 작성하려는 메서드의 구현이 Object 클래스의 기능만으로 충분할 때
- 제네릭 클래스의 메서드를 작성할때 메서드의 구현이 제네릭 클래스의 타입 인자에 의존적이지 않은 경우
- e.g. List.size or List.claer
시나리오 1. 작성하려는
printList(List )
의 구현은 Object
클래스의 toString
메서드 만으로 충분함public static void printList(List<Object> list) { for (Object elem : list) System.out.println(elem + " "); System.out.println(); }
printList 는 어느 타입이라도 루프를 돌며 출력하고 싶다.
하지만 위의 구현은 그 목적을 달성할 수 없다. 앞서 학습하였던 제네릭과 서브타입에 따라 List<Object>는 List<Integer> 타입을 받을 수 없고 해당 메서드를 실행하기 위해서는 정확히 List<Object> 타입의 파라미터만 전달 받을 수 있다.

unbounded wildcard 를 통해 목적을 달성할 수 있다.
public static void printList(List<?> list) { for (Object elem: list) System.out.print(elem + " "); System.out.println(); }
임의의 구체 타입
A
에 대해서 List<A>는 List<?> 의 서브 타입이다
List<Object>
와 List<?>
의 차이를 아는것이 중요하다. 이 둘은 서브 타입에 대해서 차이를 가질 뿐만 아니라 삽입에 대해서도 차이를 가진다. List<Object>
에는 Object
혹은 아무 Object
의 서브타입 객체를 넣을 수 있지만 List<?>
에는 null
만 넣을 수 있다.Lower Bounded Wildcards
Upper Bounded Wildcards 섹션에서는 Upper Bounded Wildcards 가 unknown type 을 특정 타입의 서브 타입으로 제한한다는 것과 그것이
extends
키워드를 사용하는것을 살펴보았습니다.비슷하게 Lower Bounded Wildcards 는 unkown type을 특정 타입의 슈퍼 타입 (부모)로 제한 하고
super
키워드를 사용합니다. <? super A>
Integer 객체를 리스트에 넣는 메서드를 작성할 때 Lower Bounded Wildcards 를 활용한다면 Integer 객체를 담을 수 있는 Number, Object 타입의 파라미터도 처리할 수 있습니다.
public static void addNumbers(List<? super Integer> list) { for (int i = 1; i <= 10; i++) { list.add(i); } }
와일드 카드와 서브 타입 Wildcards and Subtyping


- 기본은 super, extends 를 모두 슈퍼 타입으로 가짐
- 끝까지 가면 꼭 unbounded wildcards 를 슈퍼 타입으로 가짐
와일드 카드 캡쳐와 헬퍼 메서드 (Wildcard Capture and Helper Methods)
컴파일러가 와일드 카드의 타입을 추론하는 경우가 있다.
이를 Wildcard Capture라고 부른다. 대부분의 경우 Wildcard Capture는 걱정하지 않아도 되지만 에러 메시지에 “capture of “ 구문이 있으면 해결해 주어야 한다.
public class WildcardError { void foo(List<?> i) { i.set(0, i.get(0));//컴파일 에러 } }
i.get(0)를 하면 Object 객체를 반환하고 이를 List<?> i에 set 할 수 없다는 에러이다. i에 넣어줄 수 있는 것은 null밖에 없다.
제네릭을 도입한 이유중 하나인 컴파일 타임의 에러 잡기를 컴파일러가 열심히 수행하고 있기 때문에 에러가 발생하는 것이다.
하지만 위 코드는 리스트의 0번째 인덱스 값을 꺼내서 0번째 인덱스에 넣는 타입에 전혀 문제가 없는 코드이다. 이경우 private 헬퍼 메서드를 통해 컴파일러에게 자신의 의도를 알릴 수 있다.
public class WildcardFixed { void foo(List<?> i) { fooHelper(i); } // Helper method created so that the wildcard can be captured // through type inference. private <T> void fooHelper(List<T> l) { l.set(0, l.get(0)); } }
Guidelines for Wildcard Use
