1 minute read

item 33 타입 안전 이종 컨테이너를 고려하라.

제네릭은 Set<E>, Map<K,V> 과 같은 컬렉션과 ThreadLocal<T>, AtomicReference <T> 같은 단일 원소 컨테이너에 흔히 사용된다. 이 모든 쓰임에서 매개변수화 되는 대상은 원소가 아닌 컨테이너 자신이며, 하나의 컨테이너에서 매개변수화 할 수 있는 타입 개수가 제한된다.

  • Set에는 원소의 타입을 뜻하는 단 하나의 타입 매개변수만 있으면 된다.
  • Map에는 키와 값의 타입을 위한 2개만 필요하다.

하지만 때로는 더 유연한 수단이 필요할 수 있는데, 이를 위해 도입된 것이 타입 안전 이종 컨테이너 (type safe heterogeneous container pattern) 이다.

타입 안전 이종 컨테이너 (type safe heterogeneous container pattern)

  • 데이터베이스의 행은 임의 개의 열을 가질 수 있는데, 모든 열을 타입 안전하게 이용할 수 있다면 좋을 것이다. 이를 위해 컨테이너 대신 키를 매개변수화 한 다음, 컨테이너에 값을 넣거나 뺄 때 매개변수화 한 키를 함께 제공하자.
  • 이와 같은 방식을 사용하면, 제네릭 타입 시스템이 값의 타입이 키와 같음을 보장해 줄 것이다.

Favorites는 타입 안전 이종(heterogeneous) 컨테이너이다.

  • String을 요청했는데 Integer를 반환하는 일은 절대 없다.
  • 모든 키의 타입이 제각각이라, 일반적인 맵과 달리 여러 가지 타입의 원소를 담을 수 있다.
public class Favorites {    
	private Map<Class<?>, Object> favorites = new HashMap<>();    
	// 값이 아니라 키로 중첩된 와일드카드 타입을 사용하고 있다.
	// 값은 Object로 선언되어 모든 값이 키로 명시된 타입임을 보증하지 않는다.
	
	public <T> void putFavorite(Class<T> type, T instance) {        
		favorites.put(Objects.requireNonNull(type), type.cast(instance));  
		// 동적 형변환으로 타입 불변식을 어기는 일이 없도록 보장한다.
	}    
	
	public <T> T getFavorite(Class<T> type) {        
		return type.cast(favorites.get(type));    
		// favorites.get()에 의해 반환되는 객체의 타입은 Object이므로,
		// 이를 타입 변환해 반환해야 한다. (cast(): 동적 형변환 연산자)
	}
}

public static void main(String[] args) {    
	Favorites f = new Favorites();    
	
	f.putFavorite(String.class, "Java");   
	f.putFavorite(Integer.class, 0xcafebabe);   
	f.putFavorite(Class.class, Favorites.class);    
	
	String favoriteString = f.getFavorite(String.class);    
	int favoriteInteger = f.getFavorite(Integer.class);    
	Class<?> favoriteClass = f.getFavorite(Class.class);   
	
	System.out.printf("%s %x %s%n", 
		favoriteString, favoriteInteger, favoriteClass.getName());
}
  • E, List<E>, List<String>과 같은 실체화 불가 타입에는 사용이 불가하다.

  • cast() 메서드는 제너릭을 활용해, 비검사 형변환하는 손실 없이도 클래스를 타입 안전하게 만들 수 있다.

public class Class<T> {    
	public T cast(Object obj) {  
	    if (obj != null && !isInstance(obj))  
	        throw new ClassCastException(cannotCastMsg(obj));  
	    return (T) obj; 
	}
}

Favorites가 허용하는 타입을 제한하고 싶다면, 한정적 타입 토큰을 활용하면 된다.

  • 한정적 타입 매개변수(item 29)나 한정적 와일드카드(item 31)를 사용해 표현 가능한 타입을 사용하면 된다.

Leave a comment