ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 지네릭스(Generics) - 지네릭 extends, 지네릭의 한계
    백엔드/자바 2021. 5. 21. 13:13
     

    지네릭스(Generics) - 지네릭 소개, 지네릭 클래스

    지네릭스란(Generics)란? 다양한 타입의 객체를 다루는 메서드, 컬렉션에 입력 가능한 객체를 제한하게 하는 기능이다. 좀 더 직관적으로 쓰자면 특정 메서드나 컬렉션에 들어가는 자료형을 제한

    sgcomputer.tistory.com

     

    이전에 글에서 지네릭스에 대해서 설명했는데, 예제는 거기서 쭉 이어서 진행하도록 할 예정이다.

    제한적인 지네릭 타입 사용

    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라는 지네릭 클래스로 타입문자 T를 가지고 있다.

     

    그래서 해당 클래스로 객체 생성시 다양한 객체를 내부에 저장 가능했다.

     

    하지만 이런 상황이면 어떻게 해야할까?

     

     

     

     

    우선 Fruit라는 클래스와 그걸 상속받은 Apple, Grape 클래스가 존재한다.

     

    그리고 추가로 독립적인 Toy클래스가 존재한다.

     

    마지막으로 FruitBox라는 클래스가 존재하고 이 클래스는 Box클래스를 상속받았다.

     

    즉 FruitBox는 Box 클래스가 부모가 되는 자손 클래스다.

     

    그런데 우리는 이 FruitBox 클래스가 Fruit 클래스와 그 자손들만 저장하길 바란다.

     

    다시 말해 FruitBox의 지네릭 타입으로 Fruit클래스와 그 자손만 사용하게 하고 싶다.

     

     

     

    위의 코드처럼 FruitBox 객체의 지네릭 타입으로 Toy가 쓰이지 않길 원한다.

     

    하지만 이전에 배웠듯이 타입 매개변수 T는 만능 문자다.

     

    타입 매개변수T는 모든 자료 타입으로 대체가 가능하다. 이럴땐 어떻게 해야할까?

     

    이럴 땐 바로 extends를 사용하면 된다.

     

    어디에? 바로 지네릭 타입과 함께 사용하면 된다.

     

     

     

    사용법은 위와 같이 간단하다.

     

    지네릭 매개변수 T에 extends 명령어와 지네릭 타입으로 사용하고 싶은 클래스를 입력한다.

     

    extends를 써서 표기해주면 해당 객체는 거기 표기된 클래스의 객체만 저장한다.

     

    참고로 여기서 Fruit만 썼어도 Fruit의 상속 관계에 있는 Apple, Grape도 사용 가능하다.

     

    진짜 그렇게 될까? 직접 테스트를 해보자.

     

     

     

    우선 아까처럼 FruitBox로 만든 객체에는 Fruit 클래스와 상관없는 Toy는 지네릭 타입으로 올 수 없다.

     

     

     

    반면 Fruit 클래스와 그 자손들은 컴파일 에러 없이 지네릭 타입으로 지정이 가능하다.

     

    이처럼 지네릭 클래스 생성시 타입문자와 더불어 extends를 써주면 지네릭 타입 제한이 가능하다.

     

     

    추가로 지네릭 타입 제한을 클래스가 아닌 인터페이스에 쓰고 싶다면 어떻게해야할까?

     

     

    예를 들어 위와 같이 Food라는 인터페이스가 있고 Fruit는 Food를 구현하고 있다.

     

    이번에는 FruitBox의 지네릭 타입으로 Food라는 인터페이스가 오게하고 싶다.

     

     

     

    그럴때는 클래스와 마찬가지로 extends를 쓰면 된다. implements가 아니니까 햇갈리지 않도록 하자.

     

     

     

    당연히 extends로 인터페이스를 지네릭 타입으로 제한을 걸어도 컴파일 에러는 나지 않는 것을 볼 수 있다.

    지네릭스의 한계

    지네릭 클래스는 기본적으로 클래스는 수정하지 않고,

     

    객체별로 다른 타입의 자료가 저장될 수 있도록 만든 기능이다.

     

    그래서 제한되는 자료 타입도 클래스가 아닌 객체를 생성할 때 결정되는 것을 확인할 수 있다.

     

    하지만 이러한 식으로 지네릭을 사용하기 위해선 클래스 내에서 타입 매개변수를 사용해야하는데,

     

    이러한 타입 매개변수를 쓸 수 없는 상황이 크게 두 가지가 있다.

     

    하나는 static멤버와 사용될 때고, 두번째는 객체와 배열을 생성하는 new 연산자 뒤에 올 수 없다.

     

     

     

    우선 위에서처럼 static 변수의 데이터 타입으론 올 수 없다.

     

    추가로 static 메서드의 매개변수로도 타입 매개변수가 올 수 없다.

     

    왜냐면 static 멤버는 모든 객체가 하나의 static 멤버를 동일하게 공유하기 때문이다.

     

    타입 변수를 사용함으로써 객체마다 다른 객체를 저장할 수 있게 하는 것이 지네릭의 핵심이다.

     

    T는 String이 될 수도 Integer가 될 수도 있는 것이다

     

    그런데 Static이면 객체가 생성되기도 전에 이미 자료 타입이 정해져있어야 한다.

     

    만약 static이 타입 변수을 가질 수 있다면 다른 자료타입을 가진 객체들이 충돌해버린다.

     

    그렇기 때문에 Static 멤버들은 지네릭 타입 변수를 사용할 수 없는 것이다.

     

     

     

    두번째로 위와 같이 객체 혹은 배열을 생성할 때 new 연산자 다음으로 올 수 없다.

     

    타입 매개변수를 자료 타입으로서 변수 혹은 배열로 선언하는 것은 문제가 없다.

     

    하지만 이걸로 객체나 배열을 생성하기 위해 new 연산자 다음에 써준다면 그건 불가능하다.

     

    new는 뒤에 올 자료형은 자료 타입이 확정되어야 사용이 가능하기 때문이다.

Designed by Tistory.