glenn-syj / more-effective-java

이펙티브 자바를 읽으며 자바를 더 효율적으로 공부합니다
4 stars 5 forks source link

[MEJ-007] 타입 안전 이종 컨테이너에 관한 이해 보충 #149

Closed yngbao97 closed 5 days ago

yngbao97 commented 1 month ago

Based on: #147 By @undeadtimo


개인적으로 이번주 탐구 아이템 중 가장 어려운 주제라고 생각했는데, 탐구하고 정리하느라 고생하셨습니다.
이번 아이템을 이해하려고 같은 내용을 몇번이나 읽고 찾아봤는지 모르겠습니다.

저는 타입토큰에 대한 심화탐구 필요성을 느끼기 전에 타입 안전 이종 컨테이너가 왜 쓰이고 무엇을 의미하는지 알고싶었는데요, 공식 문서는 아니지만 이해하기 쉽고 원하는 답변에 가까운 내용인 것 같아 참고할만한 글을 가져왔습니다. (의문점 발견시 말씀해주시면 감사하겠습니다.)

해당 글에는 타입 안전 이종 컨테이너를 사용하지 않고, 타입별로 즐겨찾는 인스턴스를 저장하고 검색한다는 조건에 맞춰 만들기만 한 코드를 예시로 제공했습니다.

public static void main(String[] args) {

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

  favorites.put(String.class,"밥");
  favorites.put(Integer.class,"이것도 된다.");

  Integer o = (Integer) favorites.get(Integer.class); // ClassCastException
}

위 코드에서의 문제점은 키값으로 설정되는 타입토큰과 값의 타입 연관성이 보장되지 않는다는 점입니다. Integer.class를 키로 하여, "이것도 된다."라는 문자열을 값으로 추가할 수 있으며, 오류는 그 값을 조회할 때 일어납니다.

위 코드를 확인한 후, 타입 안전 이종 컨테이너를 사용한 예시 코드를 다시 살펴보면 보다 이해하기 좋습니다.

public class Main {
    public static void main(String[] args) {

        Favorites favorites = new Favorites();

        favorites.put(String.class,"밥");
        //밑에 코드는 컴파일 에러가 난다. 타입안정성을 얻을 수 있음!
        favorites.put(Integer.class,"이것도 된다.");

        //타입 형변환도 자동으로 해줘서 이런 코드가 필요 없다.
        Integer o = (Integer) favorites.get(Integer.class);
        //이렇게만 작성하면 된다.
        Integer o2 = favorites.get(Integer.class);

    }
}
//타입 안전 이종 컨테이너
class Favorites {
    private Map<Class<?>, Object> favorites = new HashMap<>();

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

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

단순히 Map 형태의 자료구조를 사용해 조건만 만족한 첫번째 예시와 달리, 별도의 Favorites 클래스를 통해 put 메서드에서 타입을 검증하고 get 메서드에서 형변환을 거치는 컨테이너 패턴을 구축하고 있습니다. 이를 통해 얻을 수 있는 이점은 1. 컴파일 타임에 타입 안전성을 보장한다는 점과 2. 원소를 조회할 때 형변환을 클라이언트 쪽에서 하지 않아도 된다는 점입니다.

책에서는 친절히 다뤄주지 않았던 타입 안전 이종 컨테이너의 단점 부분도 예시코드 예시 코드가 있으니 함께 확인해보시면 도움이 될 것 같습니다. 다시 한 번 바쁜 시기에 어려운 내용 공부하느라 고생많으셨습니다..

undeadtimo commented 1 month ago

yngbao97 님이 작성하신 글 덕분에 타입 안전 이종 컨테이너에 대해 이해가 되는군요.

오버로딩을 이용해서 또 다른 put과 get 메서드를 선언한 이후, 각 메서드 내부에서 타입 안전성을 보장할 수 있는 로직을 구현하는 것이군요.

yngbao97 님께서 말씀해주신 단점 또한 추가로 파악할 수 있어서 좋았습니다.

제가 작성한 글에서는 List 형태의 실체화 불가타입에 대한 해결책으로 슈퍼 토큰을 제시했지만, 슈퍼 토큰을 사용하는 방식 또한 완벽하지 않다는 것을 다시금 숙지할 수 있었습니다.

어려운 내용임에도 불구하고 저의 이해를 돕기 위해 훌륭한 글을 작성해주신 점 감사드립니다.