와일드카드의 종류에는 1. 비경계, 2. 한정적(상한,하한 경계) 와일드 카드가 있다.
비경계 와일드카드(Unbounded Wildcards)
- ?의 형태로 사용한다.
- 기본적으로 와일드카드는 정해지지 않은 unknown type이다.
- 예를 들어, List<?> 이다.
- 모든 타입이 인자가 될 수 있다.
- 상한 경계, 하한 경계 등 경계가 존재하지 않는다.
public static void printList(List<Object> list) {
for(Object element: list) {
System.out.println(element + " ");
}
System.out.println();
}
우리가 어떤 List의 원소들을 전부 출력해주는 함수를 만들고 싶을 때, 위와 같이 타입을 Object로 준다고 하자.
아래 코드에서 결과는
List<String> strings = new ArrayList<>();
printList(strings); // 컴파일 에러 발생
컴파일 에러가 발생한다.
이유는 임의의 타입 T의 리스트 List<T>는 List<Object>의 서브 타입이 아니기 때문이다.
해결방법
public static void printList(List<?> list) {
for(Object element: list) {
System.out.println(element + " ");
}
System.out.println();
}
아래 코드에서 결과는
List<String> strings = new ArrayList<>();
printList(strings); // 정상 출력
정상 출력되는걸 확인할 수 있다.
이유는 임의의 타입 T의 리스트 List<T>는 List<?>
비경계 와일드 카드 - Get
List<?>에서 Get한 원소는 Object 타입이다.
- 비경계 와일드 카드의 원소는 어떤 타입도 될 수 있다.
- 어떤 타입이 와도 읽을 수 있도록, 모든 타입의 공통 조상인 Object로 받는다.
public static void get(List<?> list) {
Object object = list.get(0);
String string = list.get("0"); // 컴파일 에러 발생
}
비경계 와일드 카드 - Add
List<?>에는 null만 삽입할 수 있다.
- 비경계 와일드카드의 원소가 어떤 타입인지 알 수 없다.
- 그러므로 타입 안정성을 지키기 위해 null만 삽입할 수 있다.
public static void main(String[] args) {
List<Integer> ints = new ArrayList<>();
addDouble(ints);
}
public static void addDouble(List<?> ints) {
ints.add(1.11);
}
만약 값을 추가할 수 있다면 L7: ints.add부분에서 List<Integer>에 Dobule을 추가하는 모순이 발생한다.
한정적 와일드카드(Bounded Wildcards)
class A {
}
class B extends A {
}
class C extends B {
}
이해를 돕기 위해 다음과 같은 코드가 있다고 하자.
1. 상한 경계 와일드카드(Upper Bounded Wildcards)
- extends를 사용해 와일드카드의 최상위 타입을 정의하여 상한 경계를 설정한다.
- ? extends T의 형태로 사용한다.
- 예를 들어, List<? extends T> 이다.
- T 혹은 T의 하위 클래스만 인자로 올 수 있다.
* 상한 경계 와일드 카드에서의 get
List<? extends T>에서 Get한 원소는 T이다.
- T 상한 경계 와일드 카드의 원소는 T 혹은 T의 하위 클래스이다.
- 원소들의 최고 공통 조상인 A로 읽으면, 어떤 타입이 오든 A로 읽을 수 있다.
예)
void printCollection(Collection<? extends B> collection) {
// 컴파일 에러
for (C e : collection) {
System.out.println(e);
}
for (B e : collection) {
System.out.println(e);
}
for (A e : collection) {
System.out.println(e);
}
for (Object e : collection) {
System.out.println(e);
}
}
다음 코드에서는 B로 상한 경계를 설정했기 때문에 C는 get할 수 없다.
* 상한 경계 와일드 카드에서의 add
List<? extends T>에는 null만 삽입할 수 있다.
- 상한 경계 와일드카드의 원소가 어떤 타입인지 알 수 없다.
- 동일 레벨의 다른 클래스가 들어갈 수 있다.
- 그러므로 타입 안정성을 지키기 위해 null만 삽입할 수 있다.
void addElement(Collection<? extends B> x) {
x.add(new A()); // 불가능(컴파일 에러)
x.add(new B()); // 불가능(컴파일 에러)
x.add(new C()); // 불가능(컴파일 에러)
x.add(new Object()); // 불가능(컴파일 에러)
x.add(null); // 가능
}
2. 하한 경계 와일드카드(Lower Bounded Wildcards)
- super를 사용해 와일드카드의 최하위 타입을 정의하여 하한 경계를 설정한다.
- ? super T의 형태로 사용한다.
- 예를 들어, List<? super T> 이다.
- T 혹은 T의 상위 클래스만 인자로 올 수 있다.
* 하한 경계 와일드 카드에서의 get
List<? super T>에서 Get한 원소는 Object 타입이다.
- T 하한 경계 와일드 카드의 원소는 T의 상위 클래스 중 어떤 타입도 될 수 있다.
- 어떤 타입이 와도 읽을 수 있도록, T들의 공통 조상인 Object로 받는다.
예)
void printCollection(Collection<? super B> collection) {
// 컴파일 에러
for (C e : collection) {
System.out.println(e);
}
// 컴파일 에러
for (B e : collection) {
System.out.println(e);
}
// 컴파일 에러
for (A e : collection) {
System.out.println(e);
}
for (Object e : collection) {
System.out.println(e);
}
}
* 하한 경계 와일드 카드에서의 add
List<? super T>에는 T 혹은 T의 하위 클래스만 삽입할 수 있다.
- 하한 경계 와일드카드의 원소는 T 혹은 T의 상위 클래스이다.
void addElement(Collection<? super B> x) {
x.add(new A()); // 가능
x.add(new B()); // 가능
x.add(new C()); // 불가능(컴파일 에러)
x.add(new Object()); // 불가능(컴파일 에러)
}
'언어 > JAVA' 카테고리의 다른 글
[Java] PECS(Producer-Extends, Consumer-Super) 공식 (0) | 2023.02.14 |
---|---|
[Java] 제네릭(Generic) (0) | 2023.02.13 |
[Java] 출력 3가지 방법 (0) | 2023.02.01 |
[Java] 입력 3가지 방법 (0) | 2023.02.01 |
댓글