-
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> 로도 사용할 수 있다.
핵심 정리
- 클라이언트에서 직접 형변환해야 하는 타입보다 제네릭 타입이 더 안전하고 쓰기 편하다.
- 새로운 타입을 설계할 때는 형변환 없이도 사용할 수 있도록 하라.
- 기존 타입 중 제네릭이었어야 하는게 있다면 제네릭 타입으로 변경하자.
- 기존 클라이언트에는 아무 영향을 주지 않으면서, 새로운 사용자를 훨씬 편하게 해주는 길이다.
참고 자료
'JAVA > Effective java' 카테고리의 다른 글
31. 한정적 와일드카드를 사용해 API 유연성을 높이라. (0) 2021.03.16 30. 이왕이면 제네릭타입으로 만들라2 (0) 2021.03.15 28. 배열보다는 리스트를 사용하라 (0) 2021.03.13 27. 비검사 경고를 제거하라. (0) 2021.03.12 26. 로(Raw) 타입은 사용하지 말라 (0) 2021.03.11