본문 바로가기
언어/JAVA

[Java] 와일드카드 <?>

by minhyeok.lee 2023. 2. 13.
반응형

와일드카드의 종류에는 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

댓글