ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 29. 이왕이면 제네릭 타입으로 만들라
    JAVA/Effective java 2021. 3. 14. 15:02

    • 해당 Stack 클래스는 원래 제네릭 타입이어야 마땅하다. 그러니 제네릭으로 만들어보자.
    • 지금 상태에서의 클라이언트는 스택에서 꺼낸 객체를 형변환해야 하는데, 이 때 런타임 오류가 날 위험이 있다.

     

     

    • 일반 클래스를 제네릭 클래스로 만드는 첫 단계는 클래스 선언에 타입 매개변수를 추가하는 일이다.
    • 이때 타입 이름으로는 보통 E를 사용한다. 그런 후에 Object를 적절한 타입 매개 변수로 바꾸고 컴파일 해보자.

    • 이 단계에서 대체로 하나 이상의 오류가 발생하는데 E와 같은 실체화 불가 타입으로는 배열을 만들 수 없다.
    • 배열을 사용하는 코드를 제네릭으로 만들려 할 때 이 문제는 항상 발목을 잡는다.

     

     

     

    적절한 해결책은 두 가지다.


    1. 제네릭 배열 생성을 금지하는 제약을 대놓고 우회하는 방법

     

    • Object 배열을 생성한 다음 제네릭 배열로 형변환해보자.
    • 이제 컴파일러는 오류 대신 경고를 내보낼 것이다.
    • 이렇게 할 수는 있지만 일반적으로는 타입 안전하지 않다.

    • 컴파일러는 이 프로그램이 타입 안전한지 증명할 방법이 없지만 우리는 가능하다.
    • 이 비검사 형변환이 프로그램의 타입 안정성을 해치지 않음을 우리 스스로 확인해야 한다
    • 문제의 배열 elements는 private 필드에 저장되고 클라이언트로 반환되거나 다른 메서드에 전달되는 일이 없다.
    • push 메서드를 통해 배열에 저장되는 원소 타입은 항상 E다.
    • 따라서 이 비검사 형변환은 확실히 안전하다.

     

     

     

    • 이제 안전함을 직접 증명했다면 범위를 최소로 좁혀 @Supress Warnings 애너테이션으로 해당 경고를 숨기자.

    • 배열 elements 는 push(E)로 넘어온 E 인스턴스만 담는다.
    • 따라서 타입 안정성을 보장하지만, 이 배열의 런타임 타입은 E[]가 아닌 Object[] 이다.

     

     

     

    2. elements 필드의 타입을 E[]에서 Object[]로 바꾸는 것

     

    • E는 실체화 불가 타입이므로 컴파일러는 런타임에 이뤄지는 형변환이 안전한지 증명할 방법이 없다.
    • 이번에도 마찬가지로 직접 증명하고 경고를 숨겨보자.

     

     

    • 제네릭 배열 생성을 제거하는 두 방법 모두 나름의 지지를 얻고 있다고 한다.
    • 첫 번째 방법은 가독성이 더 좋다. 배열의 타입을 E[] 로 선언하여 오직 E타입 인스턴스만 받음을 어필한다.
    • 또한 제네릭 클래스라면 코드 여러 곳에서 이 배열을 사용할텐데 첫 방식은 형변환을 배열 생성 시 한 번만 해주면 되지만, 두번째 방식에서는 배열에서 원소를 읽을 때마다 해줘야 한다.

     

     

     

     

    제네릭 Stack을 사용하는 프로그램 예


    • Stack 예처럼 대다수의 제네릭 타입은 타입 매개변수에 아무런 제약을 두지 않는다.
    • Stack<Object>, Stack<int[]>, Stack<List<String>>, Stack 등 어떤 참조타입으로도 Stack을 만들 수 있다.
    • 타입 매개변수에 제약을 두는 제네릭 타입도 있다.

     

    • 타입 매개변수 목록인 <E extends Delayed> 는 java.util.concurrent.Delayed 의 하위 타입만 받는다는 뜻이다.
    • 이렇게 하여 DelayQueue 자신과 DelayQueue를 사용하는 클라이언트는 DelayQueue의 원소에서 곧바로 Delayed 클래스의 메서드를 호출할 수 있다. 
    • 이러한 타입 매개변수 E를 한정적 타입 매개변수(bounded type parameter)라 한다. 
    • 또 한 모든 타입은 자기 자신의 하위 타입이므로 DelayQueue<Delayed> 로도 사용할 수 있다.

     

     

     

     

    핵심 정리


    • 클라이언트에서 직접 형변환해야 하는 타입보다 제네릭 타입이 더 안전하고 쓰기 편하다.
    • 새로운 타입을 설계할 때는 형변환 없이도 사용할 수 있도록 하라.
    • 기존 타입 중 제네릭이었어야 하는게 있다면 제네릭 타입으로 변경하자.
    • 기존 클라이언트에는 아무 영향을 주지 않으면서, 새로운 사용자를 훨씬 편하게 해주는 길이다.

     

     

     

     

    참고 자료 


    이펙티브 자바

Designed by Tistory.