언어/Effective Java

이펙티브 자바 Item 33 - 타입 안전 이종 컨테이너를 고려하라

now0204 2024. 6. 5. 11:42

 

타입 안전 이종 컨테이너 패턴 

 

컨테이너 대신 키를 매개변수화한 다음, 컨테이너에 값을 넣거나 뺄 때 매개변수화 키를 함께 제공하면 제네릭 타입 시스템이 값의 타입이 키와 같음을 보장해준다. 

이러한 설계 방식을 타입 안전 이종 컨테이너 패턴이라 한다. 

 

public class Favorites {
    public <T> void putFavorite(Class<T> type, T instance);
    public <T> T getFavorite(Class<T> type);
}
... // 코드생략
public static void main(String[] args) {
    Favorites favorites = new Favorites();
    favorites.putFavorite(String.class,"morning");
    favorites.putFavorite(Integer.class, 0xcafebabe);
    favorites.putFavorite(Class.class, Favorites.class);

    String favoriteString = favorites.getFavorite(String.class);
    Integer favoriteInteger = favorites.getFavorite(Integer.class);
    Class<?> favoriteClass = favorites.getFavorite(Class.class);

    System.out.printf("%s %x %s", favoriteString, favoriteInteger, favoriteClass.getName());
}

 


 

타입 안전 이종 컨테이너 구현 

[인스턴스 저장, 검색 기능]

public class Favorites {
    private Map<Class<?>, Object> favorites = new HashMap<>();
    
    public <T> void putFavorite(Class<T> type, T instance){
        favorites.put(Objects.requireNonNull(type), instance);
    } // 클래스의 리터럴 타입은 Class가 아니라 Class<T>이다.

    public <T> T getFavorite(Class<T> type){
        return type.cast(favorites.get(type));
        // Object 타입의 객체(favorites.get(type)를 꺼내 T로 바꿔 반환해야 한다.
        // cast메서드로 이 객체 참조를 Class 객체가 가리키는 타입으로 동적 형변환 한다.
    } 
    //type 정보로 값을 get하고 이를 다시 type.case해서 return 
    
}

 

public static void main(String[] args) {
    Favorites favorites = new Favorites();
    favorites.putFavorite(String.class,"morning");
    // String의 클래스 타입은 Class<String>이다.
    favorites.putFavorite(Integer.class, 0xcafebabe);
    // Integer의 클래스 타입은 Class<Integer>이다.
    favorites.putFavorite(Class.class, Favorites.class);

    String favoriteString = favorites.getFavorite(String.class);
    Integer favoriteInteger = favorites.getFavorite(Integer.class);
    Class<?> favoriteClass = favorites.getFavorite(Class.class);

    System.out.printf("%s %x %s", favoriteString, favoriteInteger, favoriteClass.getName());
}
  • Favorites 인스턴스 타입 안전하다. String타입을 요청했는데 Integer를 반환하는 일은 절대 없다! 
  • 모든 키의 타입이 제각각이라, 일반적인 맵과 달리 여러가지 원소를 담을 수 있다.
  • 따라서 이는 안전 이종 컨테이너이다. 

*컴파일 타임 타입정보와 런타임 타입 정보를 알아내기 위해 메서드들이 주고받는 class 리터럴을 타입 토큰이라고 한다. 

 

  • 핵심은 cast메서드이다. cast메서드의 반환 타입은 Class 객체의 타입 매개변수와 같다. 즉, cast 메서드는 Class 클래스가 제네릭이라는 이점을 잘 활용한다. 
public class Class<T> {
	T cast(Object object);
}