ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] 제네릭 제한된 타입 파라미터, 와일드카드
    Language/Java 2020. 8. 24. 02:03
    728x90
    반응형

    제한된 제네릭 클래스

    타입 문자로 사용할 타입을 명시하면 한 종류의 타입만 저장할 수 있도록 제한할 수 있지만, 그래도 여전히 모든 종류의 타입을 지정할 수 있다는 것에는 변함이 없다.  

    class FruitBox<T> {
        // Generic
    }
    FruitBox<Toy> fruitBox = new FruitBox<Toy>();
    fruitBox.add(new Toy());   

    그렇다면 타입 매게변수 T에 지정할 수 있는 타입의 종류를 제한할 수 있는 방법은 없을까?

     

    class FruitBox<T extends Fruit> {  // Fruit의 자손만 타입으로 지정가능
        // Logic
    }

    여전히 한 종류의 타입만 담을 수 있지만, Fruit 클래스와 같거나 자손들만 담을 수 있다는 제한이 추가 되었다. 

     

    FruitBox<Apple> appleBox = new Fruit<>();   // OK
    FruitBox<Toy> toxBox = new FruitBox<>();    // 에러. Toy는 Fruit의 자손이 아님

    그리고 만일 클래스가 아니라 인터페이스를 구현해야 한다면, 이때도 extends를 사용한다. implements를 사용하지 않는 다는 점에 주의하자. 

     

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

    그리고 또 다른 예시는 숫자를 연산하는 제네릭 메소드는 매개값으로 Number 타입 또는 하위 클래스 타입(Byte, Short, Integer, Long, Double)의 인스턴스만 가져야 한다.

     

    public <T extends Number> int compare(T t1, T t2) {
        double v1 = t1.doubleValue();
        double v2 = t2.doubleValue();
     
        return Double.compare(v1, v2);
    }

    이와 같이 숫자의 크기를 비교해야 하는 메소드에 비교할 수 있는 타입만 와야하기 때문에 extends로 제한을 하면 좋다,

     

     

     

    와일드 카드란?

    public class Juicer {
        static Juicer makeJuice(FruitBox<Fruit> box) {
            
        }
    }

    만약에 위와 같이 Juicer 클래스 안에 static 메소드 makeJuice가 존재한다고 가정하자. static 메소드기 때문에 타입 매게변수 T를 사용할 수 없으므로 아예 제네릭을 사용하지 않거나, 위와 같이 특정한 타입을 지정해줘야 한다.

     

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

    이렇게 제네릭 타입을 FruitBox<Fruit>으로 고정해 놓으면, 위의 코드에서 할 수 있듯이 FruitBox<Apple> 타입의 객체는 makeJuice()의 매게변수가 될 수 없으므로, 다음과 같이 여러 가지 타입의 매게변수를 갖는 makeJuice()를 만들 수 밖에 없다.

     

    public class Juicer {
        
        static Juicer makeJuice(FruitBox<Fruit> box) {
            
        }
        
        static Juicer makeJuice(FruitBox<Apple> box) {
            
        }
    }

    그러나 위와 같이 오버로딩하면, 컴파일 에러가 발생한다. 제너릭 타입이 다른 것만으로는 오버로딩이 성립하지 않기 때문이다. 이럴 때 사용하는 것이 '와일드 카드' 이다.

     

    또 다른 예시를 한번 살펴보자.

    public void myMethod(ArrayList list) { . . . }

    위의 메소드는 ArrayList를 매개변수로 받고 있지만, ArrayList가 담고 있는 '타입 변수'는 Number를 상속받은 객체들이어야 정상적으로 작동 하도록 되어있다. 하지만 ArrayList가 어떤 타입 변수를 가지고 있던지 간에 모두 받아들이면 안되기 때문에  이런 경우에 와일드카드를 이용하여서 ArrayList의 '타입 변수'를 지정 할 수있다.

    public void myMethod(ArrayList<? extends Number> list) { . . . }

    메소드가 받아들일 매개변수 ArrayList의 타입 변수는 Number클래스와 Number클래스를 상속받은 클래스들만 매게변수로 받을 수 있다.

     

     

    와일드 카드<?>의 제한 종류

    • <? extends T> 와일드 카드의 상한 제한(upper bound) - T와 그 자손들을 구현한 객체들만 매개변수로 가능
    • <? super T> 와일드 카드의 하한 제한(lower bound) -T와 그 조상들을 구현한 객체들만 매개변수로 가능
    • <?> 제한 없음

     

     

    Reference

    https://siyoon210.tistory.com/16

    반응형

    댓글

Designed by Tistory.