- 제네릭 타입을 조금 더 편리하게 사용할 수 있는 와일드 카드(wildcard)
- 와일드카드라는 뜻은 컴퓨터 프로그래밍에서
*,?와 같이 하나 이상의 문자들을 상징하는 특수 문자를 뜻함- 쉽게 이야기해서 여러 타입이 들어올 수 있다는 뜻
package generic.ex5;
public class Box<T> {
private T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
package generic.ex5;
import generic.animal.Animal;
public class WildcardEx {
static <T> void printGenericV1(Box<T> box) {
System.out.println("T = " + box.get());
}
static void printWildcardV1(Box<?> box) {
System.out.println("? = " + box.get());
}
static <T extends Animal> void printGenericV2(Box<T> box) {
T t = box.get();
System.out.println("이름 = " + t.getName());
}
static void printWildcardV2(Box<? extends Animal> box) {
Animal animal = box.get();
System.out.println("이름 = " + animal.getName());
}
static <T extends Animal> T printAndReturnGeneric(Box<T> box) {
T t = box.get();
System.out.println("이름 = " + t.getName());
return t;
}
static Animal printAndReturnWildcard(Box<? extends Animal> box) {
Animal animal = box.get();
System.out.println("이름 = " + animal.getName());
return animal;
}
}
?를 사용해서 정의함package generic.ex5;
import generic.animal.Animal;
import generic.animal.Cat;
import generic.animal.Dog;
public class WildcardMain1 {
public static void main(String[] args) {
Box<Object> objBox = new Box<>();
Box<Dog> dogBox = new Box<>();
Box<Cat> catBox = new Box<>();
dogBox.set(new Dog("멍멍이", 100));
WildcardEx.printGenericV1(dogBox);
WildcardEx.printWildcardV1(dogBox);
WildcardEx.printGenericV2(dogBox);
WildcardEx.printWildcardV2(dogBox);
Dog dog = WildcardEx.printAndReturnGeneric(dogBox);
Animal animal = WildcardEx.printAndReturnWildcard(dogBox);
}
}
실행 결과
T = Animal{name='멍멍이', size=100}
? = Animal{name='멍멍이', size=100}
이름 = 멍멍이
이름 = 멍멍이
이름 = 멍멍이
이름 = 멍멍이
참고사항 :
- 와일드카드는 제네릭 타입이나, 제네릭 메서드를 선언하는 것이 아님
- 와일드 카드는 이미 만들어진 제네릭 타입을 활용할 때 사용함
제네릭 메서드
static <T> void printGenericV1(Box<T> box) {
System.out.println("T = " + box.get());
}
Box<Dog> dogBox를 전달함T가 Dog가 됨일반적인 메서드(제네릭 메서드가 아님)
static void printWildcardV1(Box<?> box) {
System.out.println("? = " + box.get());
}
Box<Dog> dogBox를 전달함?는 모든 타입을 받을 수 있다.두 메서드는 비슷한 기능을 하는 코드임
와일드카드는 제네릭 타입이나 제네릭 메서드를 정의할 때 사용하는 것이 아님
Box<Dog>, Box<Cat>처럼 타입 인자가 정해진 제네릭 타입을 전달 받아서 활용할 때 사용함와일드 카드인 ?는 모든 타입을 다 받을 수 있다는 뜻
? == <? extends Object>이렇게 ?만 사용해서 제한없이 모든 타입을 받을 수 있는 와일드카드를 비제한 와일드카드라고 함
Box<Dog> dogBox, Box<Cat> catBox, Box<Object> object가 모두 입력될 수 있음//1. 전달
printGenericV1(dogBox)
//2. 제네릭 타입 결정 dogBox는 Box<Dog> 타입, 타입 추론 -> T의 타입은 Dog
static <T> void printGenericV1(Box<T> box) {
System.out.println("T = " + box.get());
}
//3. 타입 인자 결정
static <Dog> void printGenericV1(Box<Dog> box) {
System.out.println("T = " + box.get());
}
//4. 최종 실행 메서드
static void printGenericV1(Box<Dog> box) {
System.out.println("T = " + box.get());
}
//1. 전달
printWildcardV1(dogBox)
//이것은 제네릭 메서드가 아니다. 일반적인 메서드이다.
//2. 최종 실행 메서드, 와일드카드 ?는 모든 타입을 받을 수 있다.
static void printWildcardV1(Box<?> box) {
System.out.println("? = " + box.get());
}
printGenericV1()에서 제네릭 메서드를 보면, 제네릭 메서드에는 타입 매개변수가 존재함
반면에 printWildcardV1() 메서드를 보면, 와일드카드는 일반적인 메서드에 사용할 수 있고, 단순히 매개변수로 제네릭 타입을 받을 수 있는 것 뿐임
- 따라서 제네릭 타입이나 제네릭 메서드를 정의하는게 꼭 필요한 상황이 아니라면, 더 단순한 와일드 카드 사용을 권장함
static <T extends Animal> void printGenericV2(Box<T> box) {
T t = box.get();
System.out.println("이름 = " + t.getName());
}
static void printWildcardV2(Box<? extends Animal> box) {
Animal animal = box.get();
System.out.println("이름 = " + animal.getName());
}
제네릭 메서드와 마찬가지로 와일드카드에도 상한 제한을 둘 수 있음
여기서는 ? extends Animal을 지정함
Animal과 그 하위 타입만 입력 받음box.get()을 통해서 꺼낼 수 있는 타입의 최대 부모는 Animal이 됨
Animal 타입으로 조회할 수 있음결과적으로 Animal 타입의 기능을 호출할 수 있음
Box<Dog>, Box<Cat>처럼 타입 인자가 전달된 제네릭 타입을 활용할 때 사용함static <T extends Animal> T printAndReturnGeneric(Box<T> box) {
T t = box.get();
System.out.println("이름 = " + t.getName());
return t;
}
static Animal printAndReturnWildcard(Box<? extends Animal> box) {
Animal animal = box.get();
System.out.println("이름 = " + animal.getName());
return animal;
}
printAndReturnGeneric()은 다음과 같이 전달한 타입을 명확하게 반환할 수 있음Dog dog = WildcardEx.printAndReturnGeneric(dogBox)
printAndReturnWildcard()의 경우 전달한 타입을 명확하게 반환할 수 없음Animal 타입으로 반환함Animal animal = WildcardEx.printAndReturnWildcard(dogBox)
메서드의 타입들을 특정 시점에서 변경하려면 제네릭 타입이나, 제네릭 메서드를 사용해야 함
와일드카드는 이미 만들어진 제네릭 타입을 전달 받아서 활용할 때 사용함
쉽게 이야기해서 일반적인 메서드에 사용한다고 생각하면 됨
정리하면, 제네릭 타입과 제네릭 메서드가 꼭 필요한 상황이면
<T>를 사용하고, 그렇지 않은 상황이면 와일드카드를 사용하는 것을 권장함
package generic.ex5;
import generic.animal.Animal;
import generic.animal.Cat;
import generic.animal.Dog;
public class WildcardMain2 {
public static void main(String[] args) {
Box<Object> objBox = new Box<>();
Box<Animal> animalBox = new Box<>();
Box<Dog> dogBox = new Box<>();
Box<Cat> catBox = new Box<>();
// Animal 포함 상위 타입 전달 가능
writeBox(objBox);
writeBox(animalBox);
//writeBox(dogBox); // 하한이 Animal
//writeBox(catBox); // 하한이 Animal
Animal animal = animalBox.get();
System.out.println("animal = " + animal);
}
static void writeBox(Box<? super Animal> box) {
box.set(new Dog("멍멍이", 100));
}
}
실행결과
animal = Animal{name='멍멍이', size=100}
Box<? super Animal> box
?가 Animal 타입을 포함한 Animal타입의 상위 타입만 입력받을 수 있다는 뜻
- 정리하면 다음과 같음
Box<Object> objBox: 허용Box<Animal> animalBox: 허용Box<Dog> dogBox: 불가Box<Cat> catBox: 불가
Animal로 제한했기 때문에 Animal 타입의 하위 타입인 Box<Dog>는 전달할 수 없음