본문 바로가기

Java

[Java] 지네릭 클래스의 제약

제한된 지네릭 클래스

extends로 대입할 수 있는 타입을 제한

class FruitBox<T extends Fruit> { // Fruit의 자손만 타입으로 지정가능
    ArrayList<T> list = new ArrayList<T>();
    ...
}
FruitBox<Apple> appleBox = new FruitBox<Apple>(); // OK
FruitBox<Toy> toyBox = new FruitBox<Toy>(); // 에러! Toy는 Fruit의 자손이 아님

 

인터페이스인 경우도 extends를 사용

interface Eatable {}

class FruitBox<T extends Eatable> {...}

 

 

예제

public class Ex1 {

    public static void main(String[] args) {
        FruitBox<Fruit> fruitBox = new FruitBox<Fruit>(); // Fruit의 자식이고 Eatble을 구현한 객체타입만 들어올 수 있음.
        FruitBox<Apple> appleBox = new FruitBox<>();
        FruitBox<Grape> grapeBox = new FruitBox<>();

        //FruitBox<Toy> toyBox = new FruitBox<Toy>(); // 에러 타입 불일치
        //FruitBox<Grape> grapeBox1 = new FruitBox<Apple>(); // 에러
        Box<Toy> box =  new Box<Toy>(); // Box는 제한이 없고, FruitBox에는 제한이 있음, 대신 Toy만 넣을 수 있음
        Box<Fruit> fruitBox1 = new FruitBox<>();

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

        appleBox.add(new Apple());
        //appleBox.add(new Grape()); // 에러! Grape는 Apple의 자식이 아님
        //appleBox.add(new Fruit()); // 에러! Fruit는 Apple의 자식이 아님
        grapeBox.add(new Grape());

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

        System.out.println("fruitBox = " + fruitBox);
        System.out.println("appleBox = " + appleBox);
        System.out.println("grapeBox = " + grapeBox);
    }
}
class Fruit implements Eatable{
    public String toString() {
        return "Fruit";
    }
}

class Apple extends Fruit{
    public String toString() {
        return "APPLE";
    }
}
class Grape extends Fruit{
    public String toString() {
        return "GRAPE";
    }
}
class Toy {
    public String toString() {
        return "TOY";
    }
}

interface Eatable{ }

class FruitBox<T extends Fruit & Eatable> extends Box<T> { // Fruit 자손이면서 Eatable을 구현한 객체만

}

class Box<T> {
    ArrayList<T> list = new ArrayList<T>(); // item을 저장할 박스

    void add(T item){ // 박스에 아이템을 추가
        list.add(item);
    }
    T get(int i) { // 박스에서 item을 꺼낼 때
        return list.get(i);
    }
    int size() { // 박스에 들어간 item 수
        return list.size();
    }
    
    public String toString() {
        return list.toString();
    }
}
fruitBox = [Fruit, APPLE, GRAPE]
appleBox = [APPLE]
grapeBox = [GRAPE]

 

그런데 상식적으로는 말이 안되지만  Eatable이 Apple에만 구현이 되어있다면 어떨까 문득 생각했다..

class Fruit { 
    public String toString() {
        return "Fruit";
    }
}

class Apple extends Fruit implements Eatable{ // 과일을 먹지못하는데 사과를 먹는다는 모순
    public String toString() {
        return "APPLE";
    }
}
class Grape extends Fruit{
    public String toString() {
        return "GRAPE";
    }
}

 

역시 Eatable이 구현된 Apple만 FruitBox에 넣을 수 있었다!!

이유가 뭘까? 바로 이것 때문!!!!

class FruitBox<T extends Fruit & Eatable> extends Box<T> { // Fruit 자손이면서 Eatable을 구현한 객체만

 

 

지네릭스의 제약

타입 변수에 대입은 인스턴스 별로 다르게 가능

Box<Apple> appleBox = new Box<Apple>(); // Ok. Apple 객체만 저장가능
Box<Grape> grapeBox = new Box<Grape>(); // Ok. Grape 객체만 저장가능

 

1. static 멤버에 타입 변수 사용 불가

class Box<T> {
    static T time; // 에러
    static int compare(T t1, T t2) {...} // 에러
}

 

인스턴스 마다 다르게 가능한 타입변수를 공통으로 사용하는 멤버에 넣을 수 없다.

 

2. 배열,객체 생성할 때 타입 변수 사용불가, 타입 변수로 배열 선언은 가능

class Box<T> {
    T[] itemArr; // Ok. T타입의 배열을 위한 참조변수
    ...
    T[] toArray() {
        T[] tmpArr = new T[item.length]; // 에러. 지네릭 배열 생성불가(new 다음에 T는 못나옴)
   }
}

 

 

'Java' 카테고리의 다른 글

[Java] 지네릭 타입의 형변환  (0) 2024.05.10
[Java] 와일드 카드, 지네릭 메서드  (0) 2024.05.10
[Java] Iterator<E>, Map<K, V>  (0) 2024.05.09
[Java] 지네릭스란?  (0) 2024.05.09
[Java] Collections 컬렉션을 위한 메서드  (0) 2024.05.08