/ JAVAJUNGSUK

Ch12-12~14. 와일드 카드

자바의 정석 기초편

0. 목차



Chapter12. 지네릭스, 열거형, 애너테이션

Ch12 - 12. 와일드 카드

Ch12 - 13. 와일드 카드 예제

Ch11 - 14. 지네릭 메서드



Ch12 - 12. 와일드 카드


▶ 와일드 카드 < ? > 란?

▷ 하나의 참조 변수로 대입된 타입이 다른 객체를 참조 가능
  • 원래는 참조 변수의 <>와 생성자 <> 무조건 일치!
  • 와일드 카드 < ? >를 사용하면 참조 변수의 <?>와 생성자 <> 불일치 가능
      Arraylist<? extends Product> list = new ArrayList<Tv>(); // OK
      Arraylist<? extends Product> list = new ArrayList<Audio>(); // OK
      Arraylist<Product> list = new ArrayList<Audio>(); // ERROR! 매개 변수화 된 타입은 불일치
    
▷ 메서드의 매개 변수에 와일드 카드를 사용
class Fruit { }
class Apple extends Fruit { } 
static Juice makeJuice(FruitBox<? extends Fruit> box) {
    String tmp = "";
    for (Fruit f : box.getList()) tmp += f + " ";
    return new Juice(tmp);
}

System.out.println(Juice.makeJuice(new FruitBox<Fruit>())); // 와일드 카드 있든 없든 가능
System.out.println(Juice.makeJuice(new FruitBox<Apple>())); // 와일드 카드 없으면 불가능
  • 와일드 카드가 있을 때,
    • System.out.println(Juice.makeJuice(new FruitBox<Apple>()));
      = static Juice makeJuice(FruitBox<? extends Fruit> box)
      = static Juice makeJuice(FruitBox<? extends Fruit> box = new FruitBox<Apple>()))
      Fruit 포함 Fruit의 자손들 가능
  • 와일드 카드가 없을 때,
    • System.out.println(Juice.makeJuice(new FruitBox<Apple>()));
      = static Juice makeJuice(FruitBox<Fruit> box)
      = static Juice makeJuice(FruitBox<Fruit> box ≠ new FruitBox<Apple>())) // ERROR!
      Fruit만 가능

▶ 와일드 카드 < ? >의 종류

<? extends T> : 와일드 카드의 상한 제한, T와 그 자손들만 가능
<? super T> : 와일드 카드의 하한 제한, T와 그 조상들만 가능
<?> : 제한 없음, 모든 타입 가능, <? extends Object>와 동일




Ch12 - 13. 와일드 카드 예제


▶ 와일드 카드 사용하여 객체 생성 해 보기

▷ Fruit와 그 자손을 받을 수 있는 와일드 카드 사용
class Fruit implements Eatable { public String toString() {return "Fruit";} }

interface Eatable { }

class Apple extends Fruit { public String toString() {return "Apple";} }
class Grapes extends Fruit { public String toString() {return "Grapes";} }
FruitBox<? extends Fruit> fruitBox = new FruitBox<Fruit>();
▷ fruitBox에 담을 수 있는 건? Fruit, Fruit를 상속받는 Apple과 Grapes
fruitBox = new FruitBox<Fruit>();
fruitBox = new FruitBox<Grapes>();
fruitBox = new FruitBox<Apple>();


▶ Fruit에 든 과일 + 쥬스 출력

▷ Juice 붙여주는 클래스 생성 : — + “Juice”
class Juice {
	String name;

	Juice(String name) { this.name = name + "Juice"; }
	public String toString() { return name; }
}
▷ 와일드 카드로 매개 변수를 받아 list에 담긴 과일 이름 가져오기
class Juicer {
	
	static Juice makeJuice(FruitBox<? extends Fruit> box) {
		
		String tmp = " ";
		
		for(Fruit f : box.getList()) 
			tmp += f + " ";
		return new Juice(tmp);
	}
}
▷ Box에 list 전체를 출력 해 주는 메서드 추가
class Box<T> {
    ArrayList<T> list = new ArrayList<T>();
    
    void add(T item) { list.add(item); }
    T get(int i) { return list.get(i); }
    int size() { return list.size(); }
    
    ArrayList<T> getList() { return list; }
    
    public String toString() {return list.toString(); }
}
▷ 매개 변수로 과일 박스 넣어주고 돌려보기
public static void main(String[] args) {
    FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
    FruitBox<Apple> appleBox = new FruitBox<Apple>();
    FruitBox<Grapes> grapesBox = new FruitBox<Grapes>();

    fruitBox.add(new Fruit());
    fruitBox.add(new Apple());
    fruitBox.add(new Grapes());
    appleBox.add(new Apple());
    grapesBox.add(new Grapes());
    
    System.out.println(Juicer.makeJuice("fruitBox : " + fruitBox));
    System.out.println(Juicer.makeJuice("appleBox : " + appleBox));
    System.out.println(Juicer.makeJuice("grapesBox : " + grapesBox));
}

// console
fruitBox : Fruit Apple Grapes Juice
appleBox : Apple Juice
grapesBox : Grapes Juice
  • static Juice makeJuice(FruitBox<? extends Fruit> box) : Fruit의 자손들은 다 받음
  • 그래서 fruitBox, appleBox, grapesBox 다 넣기 가능



Ch12 - 14. 지네릭 메서드


▶ 지네릭 메서드란?

▷ 메서드에 타입 변수가 선언 된 것
▷ 타입 변수는 메서드 내에서만 유효
  • static <T> void sort(List<T> list, Comparator<? super T> c)
▷ 클래스 타입 매개 변수<T>와 메서드 타입 매개 변수<T>는 별개
class FruitBox<T> {
    ...
    static <T> void sort(List<T> list, Comparator<? super T> c) {
    ...
    }
}
  • 클래스 타입 매개 변수 문자 T ≠ 메서드 타입 매개 변수 문자 T
    • 같아도 다름
  • 클래스 타입 매개 변수 String vs 메서드 타입 매개 변수 Integer = 가능
  • 클래스 타입 매개 변수 String vs 메서드 타입 매개 변수 String = 가능
    • 클래스 타입 매개 변수와 메서드 타입 매개 변수는 같아도 되고 달라도 됨
  • 메서드 내에서 T가 사용 되었으면 메서드 타입 매개 변수 T!
    가까운 거 사용

▶ 지네릭 메서드 사용 시

▷ 메서드를 호출할 때마다 타입을 대임해야 함
▷ 대부분 생략 가능
FuitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FuitBox<Apple> appleBox = new FruitBox<Apple>();
...
static <T extends Fruit> Juice makeJuice(FruitBox<T> box) { // Fruit과 그의 자손(Apple) 대입 가능
String tmp = " ";

for(Fruit f : box.getList()) tmp += f + " ";

return new Juice(tmp);
}
...
System.out.println(Juicer.<Fruit>makeJuice(fruitBox)); // 메서드에 타입<Fruit>을 붙여 호출
System.out.println(Juicer.<Apple>makeJuice(appleBox)); // 메서드에 타입<Apple>을 붙여 호출
  • 호출 시 타입을 작성하지 않아도 어떤 타입인지 알 수 있음 → 타입 생략 가능
▷ 단, 메서드 호출 시 타입 작성하면 클래스 이름 생략 불가
System.out.println(<Fruit>makeJuice(fruitBox)); // ERROR! 타입 작성 시 클래스 이름 생략 불가!
System.out.println(this.<Fruit>makeJuice(fruitBox)); // OK
System.out.println(Juicer.<Fruit>makeJuice(fruitBox)); // OK


▶ 지네릭 메서드 ≠ 와일드 카드

▷ 지네릭 메서드
  • 메서드를 호출할 때마다 다른 지네릭 타입을 대입할 수 있게 한 것
      static <T extends Fruit> Juice makeJuice(FruitBox<T> box) {
      String tmp = " ";
        
      for(Fruit f : box.getList()) tmp += f + " ";
        
      return new Juice(tmp);
      }
    
▷ 와일드 카드
  • 하나의 참조 변수로 서로 다른 타입이 대입 된 여러 지네릭 객체를 다루기 위한 것
      static Juice makeJuice(FruitBox<? extends Fruit> box) {
      String tmp = " ";
        
      for(Fruit f : box.getList()) tmp += f + " ";
        
      return new Juice(tmp);
      }
    

와일드 카드를 사용할 수 없으면 지네릭 메서드를 만듦