Java

[Effective Java] 5-3. 제네릭: 고려 사항

noahkim_ 2024. 12. 30. 20:40

조슈아 블로크 님의 "Effective Java" 책을 정리한 포스팅 입니다.

 

1. 제네릭과 가변인수를 함께 쓸 때는 신중하라

제네릭 varargs 배열 매개변수에 값을 저장하는 것은 안전하지 않음

  • 가변인수는 내부적으로 타입의 배열을 생성함
  • 제네릭 타입의 배열은 생성 불가
    • 생성 시, 힙 오염 발생

 

힙 오염

public class Example {
    public static <T> void unsafeMethod(T... args) {
        Object[] array = args; // args는 T[] 타입 배열로 생성됨
        array[0] = "String";   // 런타임에는 Object로 처리되어 컴파일 가능
        T t = args[0];         // ClassCastException 발생 가능
    }

    public static void main(String[] args) {
        unsafeMethod(1, 2, 3); // <T>는 Integer로 추정되지만 String 삽입 가능
    }
}
  • 제네릭 타입의 배열 생성이나 사용에서 발생하는 문제
  • 배열이 타입 안정성을 맇고 잘못된 타입의 객체를 저장하거나 반환하게 되는 현상

 

@SafeVarargs

  • 컴파일러에 이 메서드는 가변인수 배열을 안전하게 처리한다는 것을 알림

 

사용 조건
  • 인자는 모두 같은 타입이여야 함
  • private 메서드 or, static 메서드 or final 메서드여야 함

 

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

  • 다양한 타입의 값을 안전하게 저장하고 조회할 수 있도록 설계된 컨테이너
  • 제네릭 타입 시스템이 값의 타입이 키과 같음을 보장

 

타입 토큰

Class<String> stringClass = String.class;
Class<Integer> integerClass = Integer.class;
  • 컴파일 타임에 제공되는 타입 정보를 런타임까지 보존하는 방법
  • 클래스 리터럴을 통해 타입 토큰을 표현

 

Favorites

public class Favorite {
    private Map<Class<?>, Object> favorites = new HashMap<>();
    
    public <T> void putFavorite(Class<T> type, T instance) {
        favorites.put(Objects.requireNoneNull(type), instance);
    }

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

// 다양한 타입의 값 저장
favorites.putFavorite(String.class, "Hello, World!");
favorites.putFavorite(Integer.class, 42);
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.println(favoriteString);  // 출력: Hello, World!
System.out.println(favoriteInteger); // 출력: 42
System.out.println(favoriteClass);   // 출력: class Favorites