/ JAVAJUNGSUK

Ch12-9~11. 지네릭 클래스

자바의 정석 기초편

0. 목차



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

Ch12 - 9. 제한된 지네릭 클래스

Ch12 - 10. 제한된 지네릭 클래스 예제

Ch11 - 11. 지네릭스의 제약



Ch12 - 9. 제한된 지네릭 클래스


▶ 제한된 지네릭 클래스란?

▷ extends로 대입할 수 있는 타입을 제한
  • T가 extends 하는 클래스의 자손만 타입으로 지정가능
      class FruitBox<T extends Fruit> { 
          ArrayList<T> list = new ArrayList<T>();
      }
    
    • <T> : 모든 타입 가능
    • <T extends Fruit> : Fruit포함, Fruit의 자손 타입만 가능
      FruitBox<Apple> appleBox = new FruitBox<Apple>(); // OK
      FruitBox<Toy> toyBox = new FruitBox<Toy>(); // ERROR! Toy는 Fruit 자손 아님
    
▷ 인터페이스도 implements가 아닌 extends 사용
interface Eatable { }

class FruitBox<T extends Eatable> { ... } // interface를 구현하는데도 extends 사용



Ch12 - 10. 제한된 지네릭 클래스 예제


▶ 필요한 클래스 생성 : 자기 클래스 이름 반환

▷ interface Eatable { }
▷ Eatable을 구현하는 Fruit { }
▷ Fruit을 상속 받는 Apple { }
▷ Fruit을 상속 받는 Grapes { }
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";} }
class Toy { public String toString() { return "Toy";}}


▶ FruitBox 클래스 생성

FruitBoxFruit의 자손을 제네릭스로 받으며 Box를 상속받음
class FruitBox<T extends Fruit & Eatable> extends Box<T> { }


▶ Box 클래스 생성

T(Object)를 타입 변수로 받는 제네릭 클래스
ArrayList<T> 생성
▷ FruitBox에 add하면 여기의 list.add()를 통해 list에 추가
▷ FruitBox에서 get하면 여기의 list.get()을 통해 list에 있는 거 꺼내 줌
▷ FruitBox에서 size재면 여기의 list.size()를 통해 list 사이즈 알려 줌
▷ FruitBox에서 toString하면 여기의 list.toString()를 통해 list 내용 String으로 보여 줌
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(); }
	
	public String toString() {return list.toString(); }
}


▶ 제네릭스를 사용하여 Box 객체 생성

FruitBox<Fruit>
FruitBox<Apple>
FruitBox<Grapes>
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<Apple> appleBox = new FruitBox<Apple>();
FruitBox<Grapes> grapesBox = new FruitBox<Grapes>();


▶ 생성한 Box 객체에 과일 객체 add()

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는 Fruit의 자손 다 가능 : Fruit, Apple, Grapes
▷ appleBox Apple만 가능 : 자손 없음
▷ fruitBox는 Grapes만 가능 : 자손 없음
fruitBox.add(new Fruit());
fruitBox.add(new Apple());
fruitBox.add(new Grapes());
appleBox.add(new Apple());
grapesBox.add(new Grapes());
▷ 전체
package baek;

import java.util.ArrayList;

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";} }

class Play {
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("fruitBox : " + fruitBox);
		System.out.println("appleBox : " + appleBox);
		System.out.println("grapesBox : " + grapesBox);
	}
}

class FruitBox<T extends Fruit & Eatable> extends Box<T> {

}

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(); }
	
	public String toString() {return list.toString(); }
}

// console
fruitBox : [Fruit, Apple, Grapes]
appleBox : [Apple]
grapesBox : [Grapes]


▶ 왜 에러가 나는가?

// ①
FruitBox<Grape> grapeBox = new FruitBox<Apple>();

// ②
FruitBox<Toy> toyBox = new FruitBox<Toy>();

// ③
appleBox.add(new Grapes());
▷ ① Grape - Apple 타입 불일치
▷ ② Toy - Fruit을 상속 받지 않음
▷ ③ appleBox에는 Apple 객체만 가능, Grapes 객체 불가능



Ch12 - 11. 지네릭스의 제약


▶ 타입 변수에 대입은 인스턴스 별로 다르게 가능 : <>안에 다른 타입 대입 가능

Box<Apple> appleBox = new Box<Abble>();
Box<Grapes> GrapesBox = new Box<Grapes>();
▷ static 멤버에 타입 변수 사용 불가
class Box<T> {
    static T item;
    static int compare(T t1, T t2) { ... } // 에러
}
▷ 배열 생성 시, 타입 변수 사용 불가
▷ 단, 타입 변수로 배열 선언은 가능
  • 배열 선언
      class Box<T> {
          T[] itemArr; // OK! T타입의 배열을 위한 참조 변수
      }
    
  • 배열 생성
      class Box<T> {
          T[] toArray() {
              T[] tmpArr = new T[itemArr.length]; // ERROR! 지네릭 배열 생성 불가 
          }
      }
    
    • new 연산자 뒤에는 확실한 것만! 타입 변수처럼 바뀔 수 있는 건 안됨