Skip to content

Latest commit

 

History

History
65 lines (44 loc) · 3.36 KB

33_타입_안전_이종_컨테이너를_고려하라_최락준.md

File metadata and controls

65 lines (44 loc) · 3.36 KB

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

1. 타입안전 이종 컨테이너가 뭘까?

상당히 위협적인 이름을 갖고 있다. 영어 이름 type-safe hetrogeneous container 또한 만만치 않다. 그렇다면 이러한 타입안전 이종 컨테이너가 어떤 패턴을 의미하는지 하나씩 살펴 보자.

  • type-safe (타입 안전) : generic을 통해 타입 안전성이 보장된다.
  • hetrogeneous (이종) : 서로 다른 타입이 하나의 컨테이너에 존재할 수 있음을 뜻한다.
  • container (컨테이너) : 무언가를 담고 있는 객체를 뜻한다. 쉽게 Map이나 Set등의 컬렉션이나 박싱 클래스 등을 예로 들 수 있다.

2. 그래서 왜 쓸까?

일반적인 제네릭에서는 타입의 수가 정해져 있다. 예를 들어 Map<K, V>에서는 설정된 V타입만 값으로 저장할 수 있다.

따라서 파라미터화된 제네릭 Map<String, Integer>가 있다면 Integer 이외의 값을 담을 수 없다. 이렇게 제네릭 타입의 수가 정해져 있는 것이 일반적이고, 실제로 문제 없이 작동한다.

하지만 이 Map에 Integer뿐 아니라 String, Double, Boolean 타입도 함께 답고 싶을 때는 어떻게 할까?

바로 이와 같은 경우 (서로 다른 타입을 하나의 컨테이너에 안전하게 보관)
사용할 수 있는 것이 바로 타입안전 이종 컨테이너이다.

3. 어떻게 사용할까?

방법은 간단하다. 바로 컨테이너를 매개변수화 하지 않고 컨테이너의 키를 매개변수화 하면 된다.

Map<String, Integer> : 컨테이너를 매개변수화 했다.

Map<Class<?>, Object> : 컨테이너의 키를 매개변수화 했다.

다음은 타입별로 즐겨 찾는 인스턴스를 저장하고 검색할 수 있는 Favorites 클래스이다.

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));
}

4. 어떻게 가능한 걸까?

이러한 구조가 가능한 것은 Class클래스가 제네릭이기 때문이다. 이 favorites맵의 키에는 class 리터럴이 들어가고, 값으로는 Object타입이 들어간다.

class 리터럴?

리터럴이란 변수에 할당되는 상수를 뜻한다.
class 리터럴은 Class타입의 상수를 뜻하며, 그 예로는 String.class, Integer.class, Member.class 등이 있다.
특히 이 중, 제네릭에서 매개변수화된 타입으로 쓰일 때 이를 타입 토큰이라 한다.

여기서 벨류 타입이 Object이기 때문에 모든 타입이 올 수 있다.
이는 벨류타입이 키로 설정된 타입과 반드시 일치한다는 것을 보증할 수 없다.
키 타입이 String.class라고 하더라도 벨류는 어떤 Object든 올 수 있기 때문이다.

하지만 이는 동적으로 형변환하는 cast메서드를 통해 타입안정성을 보장받을 수 있다. cast메서드의 리턴 타입은 Class 클래스의 타입 파라미터와 같기 때문이다. 따라서 cast는 키의 타입과 벨류의 타입이 일치하지 않을 때 ClassCastException을 던지게 되고, 이는 런타임에 이 컨테이너가 type-safe하다는 것을 보장할 수 있다.