ITEM33 타입 안전 이종 컨테이너를 고려하라
#
일반적인 제네릭 형태: 컬렉션 API#
예시대표적으로 제네릭은 컬렉션과 단일원소 컨테이너에서 흔히 사용된다.
- 컬렉션
Set<E>
,Set<K, V>
등 - 단일원소 컨테이너
ThreadLocal<T>
,AtomicReference<T>
등
#
매개변수화 대상원소가 아닌, 컨테이너 자신이다.
#
한계한 컨테이너가 다룰 수 있는 타입 매개변수의 수가 고정됨
- 컨테이너의 일반적인 용도에 맞게 설계된 것이므로 문제될 것은 없다.
#
타입 안전 이종 컨테이너 패턴type safe heterogeneous container pattern
- 컨테이너 대신 키를 매개변수화 한다.
- 컨테이너에 값을 추가/삭제 할 때마다, 매개변수화한 키를 함께 제공한다.
#
API데이터베이스의 행(컨테이너)을 표현한 DatabaseRow 타입에는,
- 제네릭 타입인
Column<T>
를 키로 사용할 수 있다.
타입별로 즐겨 찾는 인스턴스를 저장하고 검색할 수 있는 Favorites 클래스
#
Class 를 키로 사용- 직접 구현한 키 타입도 가능
- Class 가 제네릭이기 때문에 가능함
Class<T>
class 의 리터럴 타입Class<Integer>
Integer.class 의 리터럴 타입Class<String>
String.class 의 리터럴 타입
#
타입 토큰 type token컴파일타임 타입정보와, 런타임 타입정보를 알아내기 위해,
메서드들이 주고 받는 class 리터럴
#
클라이언트#
구현Map<Class<?>, Object>
#
1. 중첩 비한정적 와일드카드 타입 Map 이 아니라, 키가 와일드카드 타입이다.
- 모든 키가 서로 다른 매개변수화 타입일 수 있다는 의미
- 다양한 타입을 지원할 수 있게 된다.
Class<String>
Class<Integer>
...
#
2. Map 의 값 타입은 Object키와 값 사이의 타입 관계를 보증하지 않지만 관계가 성립함을 알고 있다.
- 키와 값 사이의 타입링크 type linkage 는 버려진다
- getFavorite 메서드에서 관계를 살린다.
#
3. cast 메서드, 동적 타입 캐스팅- 인수가 Class 객체가 알려주는 타입의 인스턴스인지 검사
- 맞다면 그대로 반환, 아니면 ClassCastException
#
제약사항#
1. 로타입 Class 객체는 Favorite 인스턴스의 타입 안전성을 깨뜨린다.HashSet 과 HashMap 등의 일반 컬렉션 구현체에도 같은 문제가 있다.
동적 형변환으로 런타임 타입 안전성을 확보할 수 있다.
예시) java.util.Collections
의 checkedSet, checkedList, checkedMap 메서드
#
2. 실체화 불가 타입에는 사용할 수 없다List<String>
용 Class 객체 List<String>.class
를 얻을 수 없기 때문
String
,String[]
저장 가능List<String>
저장 불가 → 컴파일 오류
List<String>
과 List<Integer>
는 List.class
라는 같은 클래스 객체를 공유한다.
- 다른타입의 리스트가 똑같은 타입의 객체 참조를 반환하는 문제
완벽한 우회로는 없지만 수퍼 타입 토큰(super type token) 방법이 있다.
- 스프링 프레임워크에서는
ParameterizedTypeReference
클래스로 구현함
#
한정적 타입 토큰Favorites 의 비한정적 타입토큰은 어떤 Class 객체든 수용한다.
#
한정적 타입 토큰을 사용하는, 애너테이션 API[39]AnnotatedElement 인터페이스의 메서드이다.
- 대상 요소에 달려있는 애너테이션을 런타임에 읽어오는 기능을 한다.
asSubclass 메서드
- 호출된 인스턴스 자신의 Class 객체를 인수가 명시한 클래스로 형변환
- 형변환 된다는 것은 인수로 명시한 클래스의 하위 클래스 라는 것임.