티스토리 뷰

반응형

제네릭은 단일원소 컨테이너에 흔히 쓰인다. (매개변수화할 수 있는 타입의 수가 제한됨)

Map<Integer, String> 에는 String 타입의 값만 담을 수 있다.

그런데, 다른 타입들도 함께 담고 싶을 때는 어떻게 할까?

→ 타입 안전 이종 컨테이너 (서로 다른 타입을 하나의 컨테이너에 안전하게 보관)

 

타입 안전 이종 컨테이너

각 타입의 Class 객체를 매개변수화한 키 역할로 사용하면 된다.(Class가 제네릭이므로 가능)

private Map<Class<?>, Object> favorites = new HashMap<>();

public <T> void putFavorites(Class<T> type, T instance) {
    favorites.put(Objects.requireNonNull(type), instance);
}

public <T> T getFavorite(Class<T> type) {
    return type.cast(favorites.get(type));
}

맵의 값 타입이 Object 이므로 모든 타입이 들어올 수 있다.

이는 값 타입이 키로 설정된 타입과 반드시 일치한다는 것을 보증할 수 없다.

 

하지만, getFavorite 메서드에서 cast 메서드를 통해 보장할 수 있다.

값 타입이 Object 이므로 T로 바꿔 반환하기 위해 cast 메서드로 캐스팅해줘야한다.

cast 메서드는 주어진 인수가 Class 객체가 알려주는 타입의 인스턴스인지 검사한다음 맞으면 그대로 캐스팅하고

아니면, ClassCastException을 발생시킨다.

 

 

타입 안전 이종 컨테이너의 제약

1) 악의적인 클라이언트가 Class 객체를 로 타입으로 넘기면 타입 안전성이 깨짐

f.putFavorite((Class)Integer.class, "hi");
int x = f.getFavorite(Integer.class);  //ClassCastException

putFavortie 메서드에서 cast 메서드로 타입이 같은지 확인하여 해결

public <T> void putFavorites(Class<T> type, T instance) {
    favorites.put(Objects.requireNonNull(type), type.cast(instance));
}

 

 

2) 실체화 불가 타입에는 사용할 수 없음

f.putFavorite(List<Integer>.class, "hi");  //컴파일 오류

List.class 와 List.class 는 List.class 라는 같은 클래스 객체를 공유하므로 아수라장

 

 

결론

  • 컬렉션 API로 대표되는 일반적인 제네릭 에서는 한 컨테이너가 다룰 수 있는 타입 매개변수 수가 고정되어 있음
  • 그러나, 컨테이너 자체가 아닌 키를 타입 매개변수로 바꾸면 이런 제약이 없는 타입 안전 이종 컨테이너를 만들 수 있음
  • 타입 안전 이종 컨테이너는 Class 를 키로 사용하며 이를 타입 토큰이라 한다.
반응형
댓글
반응형
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday