본문 바로가기
java

Generics 04 제한된 제네릭 클래스(제네릭스의 제약)

by 킹차니 2022. 1. 3.

 

제한된 제네릭

-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>(); //ERROR!

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

interface Eatable{...}
class FruitBox<T extends Eatable> {...} 
// FruitBox는 Eatable 인터페이스를 구현한 타입으로만 생성 가능하다

 

EX) 

아래와 같은 클래스들이 존재한다.

public interface Eatable {}
public class Fruit implements Eatable{
    @Override
    public String toString() {return "Fruit";}
}
public class Apple extends Fruit{
    @Override
    public String toString() {return "Apple";}
}
public class Cherry extends Fruit{
    @Override
    public String toString() {return "Cherry";}
}
public class Toy {
    public String toString() {return "Toy";}
}

 

그리고 이들을 담을 수 있는 Box클래스와 FruitBox클래스가 있다.

import java.util.ArrayList;

public class Box<T>{

    ArrayList<T> list = new ArrayList<T>();

    public void add(T item){list.add(item);}

    T get(int i){return list.get(i);}

    int size(){return list.size();}

    @Override
    public String toString() {
        return list.toString();
    }
}
public class FruitBox <T extends Fruit & Eatable> extends Box<T>{}

Box<T> 는 모든 타입들로 생성할 수 있지만, FruitBox<T extends Fruit & Eatable> extends Box<T> 는 Fruit을 extends하고, Eatable을 implements한 타입들로만 생성가능하다.

Apple과 Cherry는 이미 Fruit을 extends하고 있고, Fruit은 Eatable을 Implements하므로, Apple과 Cherry는 FruitBox를 생성할 수 있다.

 

예시 코드를 보자.

import lambda.nam.generics.fruitExample.*;

public class EX05 {
    public static void main(String[] args) {
        FruitBox<Fruit> fruitBox = new FruitBox<>(); //모든 Fruit을 담을 수 있다.
        FruitBox<Apple> appleBox = new FruitBox<>(); //Apple은 Fruit을 상속받으므로 OK
        FruitBox<Cherry> cherryBox = new FruitBox<>(); /Cherry는 Fruit을 상속받으므로 OK
        /*ERROR*/ FruitBox<Cherry> cherryAppleBox = new FruitBox<Apple>(); //ERROR 타입 불일치
        Box<Toy> toyBox = new Box<Toy>(); //Box는 모든 타입 가능
        /*ERROR*/ FruitBox<Toy> toyBox = new FruitBox<Toy>();//FruitBox는 Fruit을 extends, Eatable을 implements해야함

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

        appleBox.add(new Apple());
        cherryBox.add(new Cherry());
    }
}

 

 

 

 

제네릭스의 3가지 제약

 

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

Box<Apple> appleBox = new Box<Apple>(); // OK. Apple객체만 가능
Box<Cherry> cherryeBox = new Box<Cherry>(); // OK. Cherry객체만 가능

 

-2. static 멤버에 타입 변수 사용 불가 (static은 모든 인스턴스에 공통)

class Box<T>{
	static T item; // ERROR
	static int compare(T t1, T t2({...} // ERROR
	...
}

 

-3. 배열 생성할 때 타입 변수 사용불가. 타입 변수로 배열 선언은 가능. (new 다음에 T가 올 수 없다.)

class Box<T>{
	T[] itemArr; // OK. T타입의 배열을 위한 참조변수
	T[] toArray(){
		T[] tmpArr = new T[itemArr.length]; // ERROR! 제네릭 배열 생성 불가
		...
	}		
}

 

 

출처: 남궁성님 유튜브 강의
https://www.youtube.com/user/MasterNKS