Java - 2. Generics - 제한된 타입 파라미터, 와일드카드

갓김치·2020년 9월 17일
0

고급자바

목록 보기
9/47

제한된 타입 파라미터

  • Bounded Type Parameter
  • Generic을 이용한 타입(문자)에 대해 구체적으로 타입을 제한할 경우에 사용함.
  • <T extends 제한타입> => 제너릭타입의 상한 제한. 제한타입과 그 자손(타입)들만 가능

예시

1단계: Util 클래스 생성

Class Util2 {       //┌─▶ Number 포함 Number를 상속받은 짜식들 모두 가능
  public static <T extends Number> int compare(T t1, T t2) {
    double v1 = t1.doubleValue();
    double v2 = t2.doubleValue();

    return Double.compare(v1, v2);
  }
}

2단계: 제한된 타입 파라미터 사용 예시

public class T04_GenericMethod {
  public static void main(String[] args) {

    int result1 = Util2.compare(10, 20); // AutoBoxing(Integer)
    System.out.println(result1); // -1

    int result2 = Util2.compare(3.14, 3);
    System.out.println(result2); // 1
    
    //Util2.compare("C", "JAVA"); // String은 Number의 자손이 아니기때문에 에러
  }
}

와일드카드 (Wildcard)

  • 제너릭이 사용된 객체를 참조할 때 참조할 객체의 타입을 제한하기 위해 사용한다.
  • < ? extends T > : 와일드 카드의 상한 제한. T와 그 자손들만 가능
  • < ? super T > : 와일드 카드의 하한 제한. T와 그 조상들만 가능
  • < ? > : 모든 타입이 가능 (= <? extends Object>)

예시

과일상자 담기

1단계: 과일 클래스와 사과, 포도 클래스

class Fruit {
  private String name; // 과일이름

  //  필드값 이용한 생성자와 게터, 세터 생성
  //  toString() 오버라이드
  // 실제 파일에는 있으나 여기엔 생략
}

//사과
class Apple extends Fruit {
  public Apple() {
      super("사과");
  }
}

//포도
class Grape extends Fruit {
  public Grape() {
      super("포도");
  }
}

2단계: 과일상자 클래스

class FruitBox<T> {
  private List<T> fruitList;
  
  // 생성자
  public FruitBox() {
      fruitList = new ArrayList<>();
  }
  // 게터
  public List<T> getFruitList() {
      return fruitList;
  }
  // 세터
  public void setFruitList(List<T> fruitList) {
      this.fruitList = fruitList;
  }
  // 과일상자에 과일을 담는 메서드
  public void add(T fruit) {
      fruitList.add(fruit);
  }
	
}

3단계: 과일 쥬스 만드는 클래스

class Juicer {
//static void makeJuice(FruitBox<Fruit> box) // -> Apple이 상속받았어도 오직 Fruit만 가능
//static <T> void makeJuice(FruitBox<T> box) { // for (T f : box.getFruitList()) { 
//static void <T extends Fruit> void makeJuice(FruitBox<T extends Fruit> box( {
  static void makeJuice(FruitBox<? extends Fruit> box) {
    String fruitStr = ""; // 과일목록

    int cnt = 0;
    for (Fruit f : box.getFruitList()) {
      if (cnt == 0) {
        fruitStr += f;
      } else {
        fruitStr += "," + f;
      }
      cnt++;
    }
  System.out.println(fruitStr + " => 쥬스 완성!!!");

  }
  
//static void makeJuice(FruitBox<Apple> box) {
//-> 최종 컴파일시 FruitBox<Fruit>이건, FruitBox<Apple>이건
//   똑같은 FruitBox로 컴파일되기때문에 오버로드 할 수 없음
}

4단계: main()에서 실행

FruitBox<Fruit> fruitBox = new FruitBox<>(); // 과일상자
FruitBox<Apple> appleBox = new FruitBox<>(); // 사과상자

fruitBox.add(new Apple());
fruitBox.add(new Grape());

appleBox.add(new Apple());
appleBox.add(new Apple()); // 포도 못넣음

Juicer.makeJuice(fruitBox); // 과일 상자인 경우에는 아무런 문제 없음
// 출력: 과일 [과일이름=사과],과일 [과일이름=포도] => 쥬스 완성!!!

Juicer.makeJuice(appleBox);
// 출력: 과일 [과일이름=사과],과일 [과일이름=사과] => 쥬스 완성!!!

과일상자 담기2

1단계: 과일상자2 클래스 생성

class FruitBox2 <T extends Fruit> {
	
  List<T> itemList = new ArrayList<>();

  public void addItem(T item) {
      this.itemList.add(item);
  }

  public List<T> getItemList() {
      return itemList;
  }

  public void setItemList(List<T> itemList) {
      this.itemList = itemList;
  }
	
}

2단계: main() 에서 객체생성 해보기

// 1. 꺽쇠 있으나 없으나 동일함
  FruitBox2<?> fruitBox1 = new FruitBox2();
  FruitBox2<?> fruitBox2 = new FruitBox2<>();
//FruitBox2<? extends Fruit> fruitbox2 = new FruitBox2<Fruit>();
//-> 이미 FruitBox2클래스가 <T extends Fruit>이기 때문에 굳이 다 써줄 필요 없음.

// 2. 가능
  FruitBox2<?> fruitBox3 = new FruitBox2<Fruit>();
  
// 3. Apple은 Fruit을 상속받고 있기 때문에 가능
  FruitBox2<? extends Fruit> fruitBox4 = new FruitBox2<Apple>();

// 틀린 예 모음
//1. 
//FruitBox2<?> fruitBox5 = new FruitBox2<Object>();
//-> FruitBox2<?> 는 FruitBox2<? extends Fruit>를 의미함
//   Object는 제너릭 범위를 벗어나기때문에 사용 불가.

//2.
//FruitBox2<Object> fruitBox6 = new FruitBox2<Fruit>();
//--> 좌변, 우변 타입이 일치하지 않음
//--> 좌변에서 Object는 Object를 상속한 애들이 아니고 진짜 그 Object 1개를 의미함.
//    상속 고민할 필요 없이 그냥 타입 제한만 생각하면 됨.

//3.
//FruitBox2<? extends Object> fruitBox7 = new FruitBox2<? extends Object>();
//--> new 연산자는 타입이 명확해야 객체생성을 할 수 있다. (와일드카드 사용 불가)

수강 코스 등록예시는 T07 참고

profile
갈 길이 멀다

0개의 댓글