본문 바로가기

프로그래밍/Java

Java_제네릭스·지네릭스(2)_제네릭스의 제약, 와일드 카드, 제네릭 메서드, 제네릭 타입의 형변환

반응형

- 제네릭스의 제약

  • static 멤버에 타입변수 사용불가.  
class Box<T> {
    static T item;	//에러
    static int compare(T t1, T t2) {...}	//에러
    ...
}

→ 모든 객체에 동일하게 동작해야하는 static멤버에 타입 변수 T를 사용할 수 없다.

T는 인스턴스 변수로 간주되기 때문이다. 알다시피 static멤버는 인스턴스변수를 참조할 수 없다.

 

  • 배열 생성할 때 타입변수 사용불가, 타입 변수로 배열 선언은 가능.
class Boc<T> {
    T[] itemArr; //OK. T타입의 배열을 위한 참조변수
        ...
    T[] toArray(){
        T[] tmpArr=new T[itemArr.length]; //에러. 지네릭 배열 생성불가.
        ...
        return tmpArr;
    }
        ...
}

→ 지네릭 배열을 생성할 수 없는 것은 new연산자 때문인데, 이 연산자는 컴파일 시점에 타입 T가 정확히 뭔지 알아야 한다. 그런데 위의 코드에 정의된 Box<T>클래스를 컴파일하는 시점에서는 T가 어떤 타입이 될지 알 수 없기 때문에 타입변수로 사용이 불가하다.

 

 

 

- 와일드 카드

: 제네릭 클래스를 생성할 때, 참조변수에 지정된 제네릭 타입과 생성자에 지정된 제네릭 타입은 일치해야 한다.

ArrayList<Tv> list=new ArrayList<Tv>(); //OK. 제네릭 타입 일치
List<Tv> list=new ArrayList<Tv>(); //OK. 다형성. 제네릭 타입 일치

ArrayList<Product> list=new ArrayList<Tv>(); //에러. 제네릭 타입 불일치

 

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

 

와일드 카드를 이용하면 하나의 참조변수로 다른 제네릭 타입이 지정된 객체를 다룰 수 있다. 

ArrayList<? extends Product> list=new ArrayList<Tv>();
ArrayList<? extends Product> list=new ArrayList<Audio>();

→ Tv와 Audio가 Product의 자손이라고 가정

→ 제네릭 타입이 '? extends Product'이면, Product와 Product의 모든 자손이 제네릭 타입으로 가능하다.

 

<와일드 카드 예제>

import java.util.ArrayList;

class Fruit2 { public String toString() {return "Fruit";}}
class Apple2 extends Fruit2 { public String toString() {return "Apple";}}
class Grape2 extends Fruit2 { public String toString() {return "Grape";}}

class Juice {
    String name;
    
    Juice(String name) {
        this.name=name+"Juice";
    }
    
    public String toString() {
        return name;
    }
}

class JavaJungsuk_Generics_wildCard {
    public static void main(String[] args) {
        FruitBox2<Fruit2> fruitBox=new FruitBox2<Fruit2>();
        FruitBox2<Apple2> appleBox=new FruitBox2<Apple2>();
        
        fruitBox.add(new Apple2());
        fruitBox.add(new Grape2());
        appleBox.add(new Apple2());
        appleBox.add(new Apple2());
        
        System.out.println(Juicer.makeJuice(fruitBox));
        System.out.println(Juicer.makeJuice(appleBox));
    }
}

class FruitBox2<T extends Fruit2> extends Box2<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);
    }
    
    ArrayList<T> getList() {
        return list;
    }
    
    int size() {
        return list.size();
    }
    
    public String toString() {
        return list.toString();
    }
}

결과
Apple Grape Juice
Apple Apple Juice

 

 

 

- 제네릭 메서드 

: 메서드의 선언부에 제네릭 타입이 선언된 메서드를 제네릭 메서드라 한다. 

class FruitBox<T> {
        ...
    static<T> void sort(List<T> list, Comparator<? super T> c) {
        ...
    }
}

→ 제네릭 클래서 FruitBox에 선언된 타입 매개변수 T와 제네릭 메서드 sort()에 선언된 타입 매개변수 T는 타입 문자만 같을 뿐 서로 다른 것이다.

→ static멤버에 타입 매개변수를 사용할 수 없지만, sort() 메서드에 제네릭 타입을 선언하고 사용하는 것은 가능하다.

 

 

메서드를 호출할 때는 아래와 같이 타입 변수에 타입을 대입해야 한다.

FruitBox<Fruit> fruitBox=new FruitBox<Fruit>();
FruitBox<Apple> appleBox=new FruitBox<Apple>();
        ...
System.out.println(Juicer.<Fruit>makeJuice(fruitBox));
System.out.println(Juicer.<Apple>makeJuice(appleBox));

→ 대부분의 경우 컴파일러가 대입된 타입을 추정할 수 있기 때문에 생략해도 된다. 

 

System.out.println(<Fruit>makeJuice(fruitBox));	//에러. 클래스 이름 생략불가
System.out.println(this.<Fruit>makeJuice(fruitBox));	//OK
System.out.println(Juicer.<Fruit>makeJuice(fruitBox));	//OK

→ 제네릭 메서드를 호출할 때, 대입된 타입을 생략할 수 없는 경우에는 참조변수나 클래스 이름을 생략할 수 없다.

→ 같은 클래스 내에 있는 멤버들끼리는 참조변수나 클래스이름, 즉 'this.'이나 '클래스이름'을 생략하고 메서드 이름만으로 호출이 가능하지만, 대입된 타입이 있을 때는 반드시 써줘야 한다.

 

 

 

- 제네릭 타입의 형변환

Box box=null;
Box<Object> objBox=null;

box=(Box)objBox;			//OK. 제네릭 타입->원시 타입. 경고발생
objBox=(Box<Object>)box;	//OK. 원시 타입->제네릭 타입. 경고발생

→ 제네릭 타입과 원시타입간의 형변환은 항상 가능하다. 다만 경고가 발생한다.

 

Box<Object> objBox=null;
Box<String> strBox=null;

objBox=(Box<Object>)strBox;	//에러.
strBox=(Box<String>)objBox;	//에러.

→ 대입된 타입과 제네릭 타입간의 형변환은 불가능하다. 

반응형