1. 와일드 카드란?
- Juicer라는 클래스가 존재하고 static 메서드로 makeJuice()가 있다고 가정해보자.
class Juicer{
static Juice makeJuice(FruitBox<Fruit> box) { // <Fruit>으로 지정
String tmp = "";
for(Fruit f : box.getList()) tmp += f + " ";
return new Juice(tmp);
}
}
( Juice 클래스는 지네릭 클래스가 아닌데다, 지네릭 클래스라고 해도 static 메서드에는 타입 매개변수 T를 매개변수에
사용할 수 없으므로 아예 지네릭스를 적용하지 않던가, 위와 같이 같이 <Fruit>로 지정해줘야 한다. )
( 그래서 아래와 같이 fruitBox는 컴파일이 잘 되지만, appleBox에 대해서는 컴파일 에러가 발생한다. )
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<Apple> appleBox = new FruitBox<Apple>();
Juicer.makeJuice(fruitBox);
// 컴파일 정상
Juicer.makeJuice(appleBox);
// 컴파일 에러
( 그렇게 되면, 우리는 또 아래의 코드처럼 static 메서드를 생성해야 한다. )
class Juicer{
static Juice makeJuice(FruitBox<Fruit> box) { // <Fruit>으로 지정
String tmp = "";
for(Fruit f : box.getList()) tmp += f + " ";
return new Juice(tmp);
}
static Juice makeJuice(FruitBox<Apple> box) { // <Fruit>으로 지정
String tmp = "";
for(Fruit f : box.getList()) tmp += f + " ";
return new Juice(tmp);
}
}
( 그러나, 위와같이 오버로딩하면, 컴파일 에러가 발생한다. )
( 지네릭 타입이 다른 것만으로 오버로딩이 성립하지 않기 때문이다. )
( 그 이유는 지네릭 타입은 컴파일러가 컴파일할 때만 사용하고 제거하므로, 메서드 중복 정의로 간주되기 때문이다. )
( 이럴 때 사용하기 위해 고안된 것이 바로 '와일드 카드' 이다. )
( 와일드 카드는 기호 "?"로 표현하는데, 어떠한 타입도 될 수 있다. )
( "?" 만으로는 Object 타입과 다를 게 없으므로, 'extends'와 'super'로 상한과 하한을 제한할 수 있다. )
"?" 표현방식 | 설명 |
<? extends T> | 와일드 카드의 상한 제한. T와 그 자손들만 가능. |
<? super T> | 와일드 카드의 하한 제한. T와 그 조상들만 가능. |
<?> | 제한 없음. 모든 타입이 가능. <? extends Object> 와 동일함. |
( 그러므로, 와일드 카드를 사용하면 아래의 코드가 나오게 된다. )
class Juicer{
static Juice makeJuice(FruitBox<? extends Fruit> box) { // <Fruit>으로 지정
String tmp = "";
for(Fruit f : box.getList()) tmp += f + " ";
return new Juice(tmp);
}
}
( 또한 아래의 코드처럼 <? extends Object>를 수행할 수 있다. )
class Juicer{
static Juice makeJuice(FruitBox<? extends Object> box) { // <Fruit>으로 지정
String tmp = "";
for(Fruit f : box.getList()) tmp += f + " ";
return new Juice(tmp);
}
}
( 그러나 이렇게 되면 for문 안에 있는 Fruit가 에러일 수 있다. )
( 그 이유는 FruitBox의 모든 객체가 사용할 수 있기 때문이다. )
( 그러나, 실제로 컴파일을 해보면 에러가 발생하지 않는다. )
( 왜냐하면 FruitBox에서 이미 클래스에 <T extends Fruit>을 썻기 때문이다. )
( 그러므로, 이미 Fruit의 자손만 가능하므로 컴파일 에러가 발생하지 않는 것이다. )
1-1. 와일드 카드를 이해하기 위한 예제(1)
: 위에서 했던 코드들을 합친 예제이다.
import java.util.ArrayList;
public class Exercise003 {
public static void main(String[] args){
Fruit3Box3<Fruit3> Fruit3Box3 = new Fruit3Box3<Fruit3>();
Fruit3Box3<Apple3> Apple3Box3 = new Fruit3Box3<Apple3>();
Fruit3Box3.add(new Apple3());
Fruit3Box3.add(new Grape3());
Apple3Box3.add(new Apple3());
Apple3Box3.add(new Apple3());
System.out.println(Juicer.makeJuice(Fruit3Box3));
System.out.println(Juicer.makeJuice(Apple3Box3));
}
}
class Fruit3 { public String toString() { return "Fruit3";}}
class Apple3 extends Fruit3 { public String toString() { return "Apple3";}}
class Grape3 extends Fruit3 { public String toString() { return "Grape3";}}
class Juice {
String name;
Juice(String name) { this.name = name + "Juice"; }
public String toString() { return name; }
}
class Juicer {
static Juice makeJuice(Fruit3Box3<? extends Fruit3> Box3) {
String tmp = "";
for(Fruit3 f : Box3.getList())
tmp += f + " ";
return new Juice(tmp);
}
}
class Fruit3Box3<T extends Fruit3> extends Box3<T> {}
class Box3<T> {
//class Fruit3Box3<T extends Fruit3> {
ArrayList<T> list = new ArrayList<T>();
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();}
}
1-2. 와일드 카드를 이해하기 위한 예제(2)
: 'super'로 와일드 카드의 하한을 사용하여 제한하는 예제이다.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class Exercise004 {
public static void main(String[] args){
Fruit4Box4<Apple4> Apple4Box4 = new Fruit4Box4<Apple4>();
Fruit4Box4<Grape4> Grape4Box4 = new Fruit4Box4<Grape4>();
Apple4Box4.add(new Apple4("GreenApple4", 300));
Apple4Box4.add(new Apple4("GreenApple4", 100));
Apple4Box4.add(new Apple4("GreenApple4", 200));
Grape4Box4.add(new Grape4("GreenGrape4", 400));
Grape4Box4.add(new Grape4("GreenGrape4", 300));
Grape4Box4.add(new Grape4("GreenGrape4", 200));
Collections.sort(Apple4Box4.getList(), new Apple4Comp());
Collections.sort(Grape4Box4.getList(), new Grape4Comp());
System.out.println(Apple4Box4);
System.out.println(Grape4Box4);
System.out.println();
Collections.sort(Apple4Box4.getList(), new Fruit4Comp());
Collections.sort(Grape4Box4.getList(), new Fruit4Comp());
System.out.println(Apple4Box4);
System.out.println(Grape4Box4);
}
}
class Fruit4 {
String name;
int weight;
Fruit4(String name, int weight) {
this.name = name;
this.weight = weight;
}
public String toString() { return name+"("+weight+")";}
}
class Apple4 extends Fruit4 {
Apple4(String name, int weight) {
super(name, weight);
}
}
class Grape4 extends Fruit4 {
Grape4(String name, int weight) {
super(name, weight);
}
}
class Apple4Comp implements Comparator<Apple4> {
public int compare(Apple4 t1, Apple4 t2) {
return t2.weight - t1.weight;
}
}
class Grape4Comp implements Comparator<Grape4> {
public int compare(Grape4 t1, Grape4 t2) {
return t2.weight - t1.weight;
}
}
class Fruit4Comp implements Comparator<Fruit4> {
public int compare(Fruit4 t1, Fruit4 t2) {
return t1.weight - t2.weight;
}
}
class Fruit4Box4<T extends Fruit4> extends Box4<T> {}
class Box4<T> {
ArrayList<T> list = new ArrayList<T>();
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)
( 위와 같이 정의된 <T> 메서드를 '지네릭 메서드' 라고 부른다. )
( 위의 코드에서 'super'로 정의한 이유는 코드 중복에 대한 문제를 방지하기 위해서이다. )
( 만약 우리가 sort()에 super라는 것이 없었더라면, 위 예제에서처럼 Fruit에 대한 자손들인 Apple과 Grape를
정렬하기 위해서는 각각의 AppleComp과 GrapeComp를 만들어 정렬하였을 것이다. )
( 그러나, 이런식으로 Fruit의 자손이 생길때마다 Comp에 대한 코드를 계속 추가해야하는 문제가 발생하게 된다. )
( 그러나, Comparator<? super T>를 정의하였기 때문에, 손쉽게 FruitComp만 정의해주면 굳이 추가적으로
각각의 Comp를 만들어줄 필요가 없는 것이다. )
( 아래의 예시를 보고 이해하자. )
< Collections.sort()의 호출 >
sort(appleBox.getList(), new FruitComp());
< 실제 sort() 메서드 >
static <Apple> void sort(List<Apple> list, Comparator<? super Apple> c);
Comparator<? super T> | 올 수 있는 Comparator<> |
Comparator<? super Apple> | Comparator<Apple>, Comparator<Fruit>, Comparator<Object> |
Comparaotr<? super Grape> | Comparator<Grape>, Comparator<Fruit>, Comparator<Object> |
'자바 > 지네릭스' 카테고리의 다른 글
지네릭 타입의 형변환과 제거 (0) | 2022.05.16 |
---|---|
지네릭 메서드 (0) | 2022.05.13 |
지네릭스의 전반적 개념(2) (0) | 2022.05.11 |
지네릭스의 전반적 개념(1) (0) | 2022.05.11 |