제네릭스(Generics) - 2
와일드카드
매개변수에 과일박스(FruitBox)를 대입하면 주스를 만들어서 반환하는 Jucier 클래스가 있다고 하자. 이 클래스에는 과일을 주스로 만들어 반환하는 makeJuice()라는 static 메서드가 있다.
class Juicer { static Juice makeJuice(FruitBox<Fruit> box) { String tmp = ""; for(Fruit f : box.getList()) { tmp += f + " "; } return new Juice(tmp); } }
이 메서드의 패러미터 타입을 Fruit타입의 FruitBox 객체로 제한하였기 때문에, Fruit 이외의 타입 은 들어갈 수 없다. (Juicer는 제네릭 클래스도 아니고, 제네릭 클래스라고 하여도 static메서드에서는 T를 사용할 수가 없다) 하지만 Fruit의 자식인 Grape나 Apple 등도 집어넣고 싶다면 어떻게 해야될까?
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>(); Juicer.makeJuice(fruitBox); // OK FruitBox<Grape> grapeBox = new FruitBox<Grape>(); Juicer.makeJuice(grapeBox); // Err!
이럴 때 사용하기 위해 고안된 것이 바로 와일드카드이다. 와일드카드는 기호
?
로 나타내며 어떠한 타입도 될 수 있다.<? extends T> // T와 자손들만 가능 <? super T> // T와 조상들만 가능 <?> // 모든 타입 가능. <? extends Object>와 같음
static Juice makeJuice(FruitBox<? extends Fruit>) { ... } FruitBox<Grape> grapeBox = new FruitBox<Grape>(); Juicer.makeJuice(grapeBox); // OK!
와일드카드 예제 코드 (
Comparator
)import java.util.*; class Fruit { String name; int weight; Fruit(String name, int weight) { this.name = name; this.weight = weight; } public String toString() { return name + "("+ weight +")"; } } class Apple extends Fruit { Apple(String name, int weight) { super(name, weight); } } class Grape extends Fruit { Grape(String name, int weight) { super(name, weight); } } class AppleComp implements Comparator<Apple> { public int compare(Apple t1, Apple t2) { return t2.weight - t1.weight; } } class GrapeComp implements Comparator<Grape> { public int compare(Grape t1, Grape t2) { return t2.weight - t1.weight; } } class FruitComp implements Comparator<Fruit> { public int compare(Fruit t1, Fruit t2) { return t2.weight - t1.weight; } } class FruitBoxEx { public static void main(String[] args) { FruitBox<Apple> appleBox = new FruitBox<Apple>(); FruitBox<Grape> grapeBox = new FruitBox<Grape>(); appleBox.add(new Apple("GreenApple", 300)); appleBox.add(new Apple("GreenApple", 100)); appleBox.add(new Apple("GreenApple", 200)); grapeBox.add(new Grape("GreenGrape", 400)); grapeBox.add(new Grape("GreenGrape", 300)); grapeBox.add(new Grape("GreenGrape", 200)); Collections.sort(appleBox.getList(), new AppleComp()); Collections.sort(grapeBox.getList(), new GrapeComp()); System.out.println(appleBox); System.out.println(grapeBox); System.out.println(); Collections.sort(appleBox.getList(), new FruitComp()); Collections.sort(grapeBox.getList(), new FruitComp()); System.out.println(appleBox); System.out.println(grapeBox); } } class FruitBox<T extends Fruit> extends Box<T> {} class Box<T> { ArrayList<T> list = new ArrayList<>(); void add(T item) { list.add(item); } T get(int i) { return list.get(i); } ArrayList<T> getList() { return list; } int size() { return list.size(); } public String toString() { return list.toString(); } }
여기서
Collections.sort()
가 사용되었는데 이 메서드의 선언부는 다음과 같다.static <T> void sort(List<T> list, Comparator<? super T> c)
<? super T>
는 T와 T의 조상들이 매개변수로 올 수 있다는 뜻이다. 매개변수 타입이 Comparator<? super Apple>이라는 의미는 Comparator타입의 매개변수로 Apple과 그 조상인 Fruit, Object가 올 수 있다는 뜻이다. 그래서 FruitComp를 만들면List<Apple>
과List<Grape>
모두 정렬할 수 있다.