ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 지네릭스(Generics) - 와일드카드
    백엔드/자바 2021. 5. 21. 13:21

    우선 지네릭스에 관해서 이전에 포스팅한 내용이 있다.

     

    2021.05.21 - [백엔드/자바] - 지네릭스(Generics) - 와일드카드, 지네릭 메서드

     

    거기서 예제 코드가 이어진다. 그래서 그 코드가 있어야 아래 내용을 이해할 수 있다.

     

    시작하기 전에 간단히 코드들을 정리하면 다음과 같다.

     

     

    Box 클래스

     

    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; };
    
        @Override
        public String toString() {
            return list+"";
        }
    }

     

     

    Box를 상속받은 FruitBox 클래스

     

    class FruitBox<T extends Fruit> extends Box<T>{}

     

     

    FruitBox에 들어갈 수 있는 Fruit 클래스와 그 자손인 Apple와 Grape 클래스. 그리고 독립 클래스인 Toy

     

    class Fruit {}
    
    class Apple extends Fruit{
    
        String name = "apple";
    
        @Override
        public String toString() {
            return name+"";
        }
    }
    
    class Grape extends Fruit{
        String name = "grape";
    
        @Override
        public String toString() {
            return name+"";
        }
    }
    
    class Toy{}

     

    지네릭의 한계

    지네릭스의 사용 목적은 객체마다 다른 데이터 타입을 제한적으로 저장할 수 있게 돕는 것이다.

     

     

     

    위와 같은 지네릭 클래스가 있다고 가정해보자.

     

    위의 지네릭 클래스의 경우 객체 생성시 데이터 타입을 지정해주면,

     

    지정해준 데이터 타입만 해당 객체에 저장할 수 있다.

     

    지네릭을 사용하면 런타임 이전에 컴파일러가 이런 부분을 체크해줌으로 오류의 가능성을 줄인다.

     

    이때 핵심이 되는 것이 바로 타입 매개변수다.

     

    타입 매개 변수가 있어서 객체 생성시 다른 데이터 타입을 입력할 수 있는 것이다.

     

    하지만 이러한 타입 매개변수 사용하는데는 한계가 따른다.

     

     

     

    우선 static 변수의 데이터 타입이나 static 변수의 매개변수로는 타입 매개변수의 사용이 불가능하다.

     

    지네릭스 자체가 사용 불가능한 것은 아니다.

     

     

     

    예를 들어 위와 같이 자료 타입을 확실히 명시한 지네릭이라면 컴파일 오류가 나지 않는다.

     

    하지만 맨 처음의 예시처럼 타입 매개변수로 타입 문자를 넣는 것은 불가능하다.

     

     

     

    그리고 타입 매개변수로 문자를 넣는 것이 불가능한 또 하나의 상황은 일반 클래스에서 매개변수 사용이다.

     

    만약 지네릭 클래스가 아닌 클래스에서 내부적으로 타입 매개변수를 사용한 지네릭을 쓸 수 없다.

     

    위에서 보듯이 전부 컴파일 에러를 일으킨다.

     

    물론 일반 클래스 내에서도 자료형을 명시한 지네릭스라면 static처럼 사용이 가능하다.

    한계의 극복 와일드카드

    앞서 말했듯이 지네릭의 타입 매개변수는 두 가지 상황에서 사용할 수 없다.

     

    static 멤버의 지네릭으로 쓰일 때와 일반 클래스의 지네릭으로 쓰일 때다.

     

    하지만 쓰지말라고해도 어느 순간인가는 써야할 순간이 올지도 모른다.

     

    그럴 때를 대비해서 만든 것이 바로 와일드카드(wildCard) 기능이다.

     

     

     

    예를 들어 위와 같은 클래스가 있다고 가정해보자.

     

    해당 클래스는 wildCardTest라는 메서드를 가지고 있다.

     

    이 메서드는 Fruit클래스와 자손인 Apple, Grape클래스의 객체를

     

    저장할 수 있는 FruitBox 객체를 파라미터로 받는다.

     

     

     

    그리고나서 전달 받은 객체에 담긴 Fruit와 그 자손 객체들을 위와 같이 콘솔창에 출력한다.

     

    그런데 이 메서드는 현재로서는 실행이 불가능하다.

     

    바로 타입 매개변수 T를 사용할 수 없기 때문이다.

     

    제네릭 클래스도 아닐 뿐더러 static 메서드라서 타입 매개변수의 사용이 불가능하다.

     

    이럴 때는 어떻게 해줘야할까?


     

    위와 같이 와일드카드인 ?(물음표)를 사용하면 된다.

     

    우선 타입 매개변수 T를 ?표로 바꾸고 나니 컴파일 에러는 나지 않는다.

     

     

    그럼 실제로 실행해보자.

     

     

    wildCardTest()는 static이므로 객체를 생성할 필요없이 바로 실행하면 된다.

     

     

     

    결과는 예상한 대로 잘 나왔다.

     

     

     

     

    그리고 당연히 fruit와 상속관계에 있는 Apple과 Grape 객체를 담은 applebox, grapebox 객체도

     

    전달 가능하다.

     

    이처럼 와일드 카드 ?는 제한적인 상황이라 타입 매개변수를 사용할 수 없는 상황에 쓰인다.

     

    지네릭 안에 와일드카드가 들어가면 해당 지네릭에는 모든 객체가 저장이 가능하므로

     

    지네릭 안에 Object를 쓴 것과 마찬가지인 상황이다.

     

    그래서 와일드카드를 쓸 때는 가급적이면 범위를 잘 확인해서 제한해주는 것이 중요하다.

     

     

     

    예를 들어서 위와 같은 코드가 있다고 가정해보자.

     

     

     

    위 static 클래스는 위에처럼 Box 클래스로 만들어진 모든 객체를 담을 수 있다.

     

    하지만 Fruit 클래스와 그 자손인 Apple, Grape만 담고 싶다면 어떻게 해야할까?

     

     

     

    그럴 땐 extends를 써서 와일드카드의 범위를 제한해주면 된다.

     

     

     

    extends를 써주고 난 뒤에는 위처럼 Toy 클래스를 담은 박스는 메서드에서 못받는 것을 볼 수 있다.

     

    이처럼 와일드카드도 일반 타입 매개변수처럼 범위 제한이 가능한데 정리하면 다음과 같다.

     

     


    <? extends T>


    와일드 카드의 상한 제한. T와 그 자손들만 사용 가능


    <? super T>


    와일드 카드의 하한 제한. T와 그 조상들만 사용 가능


    <?>


    제한없음. 모든 타입 사용 가능. <?extends Object>와 동일

     

    그림으로 보면 다음과 같다.

     

     

     

    마지막으로 한 가지만 더 알아보자.

     

    기본적으로 static에서는 타입 매개변수를 쓸 수 없다고 했다.

     

     

     

    그래서 위와 같이 만약 지네릭을 쓰고 싶다면 위와 같이 자료 타입을 명시해주면 된다고 했다.

     

    그렇다면 이런 생각이 들 수 있다.

     

    FruitBox의 지네릭에 Fruit 자료형이 적혀 있으니 Fruit의 자손 클래스의 객체도 저장이 가능하지 않을까?

     

     

     

    결론부터 말하면 불가능하다.

     

     

     

    예를 들어 위와 같이 선언을 할 때는 자손까지 반영해서 범위을 잡는다.

     

    하지만 static이나 일반 메서드에서 지네릭을 쓸 때 안에 써준 자료 타입은 자손을 반영하지 않는다.

     

    앞서 말했듯 타입 매개 변수는 일반 메서드와 static에서 쓸 수 없다. 그래서 와일드 카드를 쓰는 것이다.

     

    그래서 자료형을 확정하기 위해서 자료형을 확정지어 써준건데 부모 클래스자료형을 썼다고

     

    자손까지 반영하면 어떤 자료형이 오는지 컴파일러는 알 수 없기 때문에 에러를 발생시킨 것이다.

     

    그래서 가급적이면 static을 써야할 경우에는 와일드카드를 이용하도록 하자.

Designed by Tistory.