아이템 26. 로 타입은 사용하지 말라
- 로타입은 뭐든지 다 들어간다. → 꺼낼 때, 값을 항상 올바른 타입으로 캐스팅해줘야 한다. → 타입안정성, 표현력을 잃게 된다.
- 비한정적 와일드카드 타입(ex. List<?>)은 어떤 타입이든 상관없이 받을 수 있어 타입안정성을 보장해준다.
얘는 null외에 아무것도 못 넣어서 불변식도 보장한다.
List<Object>
와 List<String>
은 공변관계 아니다. 그냥 다른 타입이라고 생각하자
- 런타임에는 제네릭 타입 정보가 지워진다!
- 로타입을 써도 좋은 곳
1. 클래스 리터럴
2. instanceof (로타입 or 비한정적 와일드카드 타입)
아이템 27. 비검사 경고를 제거하라
- 가능한 모든 unchecked warning을 제거하라
모두 제거하면 타입 안정성 보장
@SuppressWarnings
는 가능한 한 좁은 범위에 적용하자
경고를 무시해도 안전한 이유를 주석으로 남겨라
아이템 28. 배열보다는 리스트를 사용하라
- 배열은 공변이고, 제네릭은 불공변이다
공변이란?
Sub이 Super의 하위 타입이라면 Sub[]
도 Super[]
의 하위 타입이 된다 (같이 변한다)
제네릭은 불공변이라 List<Sub>
, List<Super>
는 아예 남남이다.
- 배열은 실체화(reify)된다
배열은 런타임에도 담기로한 타입을 알지만, 제네릭은 타입 정보가 런타임에는 소거된다.
- 제네릭은 컴파일 에러를 던져주니까 런타임에는 타입안전하다.
아이템 29. 이왕이면 제네릭 타입으로 만들라
- 클라이언트가 사용할 때 매번 형변환을 해야하고, 그 과정에서 런타임 에러가 나는 것을 방지하기 위해 이왕이면 제네릭 타입으로 만들자.
- 배열을 사용하는 코드를 제네릭으로 만드는 두가지 방법이 나옴
- 방법 1. Object 배열을 제네릭 배열로 형변환
E[] elements = (E[]) mew Object[10];
- 방법 2. 꺼낼 때마다 E로 형변환해주기
- 방법 1이 간단하지만, 힙 오염을 일으킬 수 있음
컴파일타임 타입 =! 런타임 타입
아이템 30. 이왕이면 제네릭 메서드로 만들라
- 타입 매개변수들을 선언하는 타입 매개변수 목록은 메서드의 제한자와 반환 타입 사이에 온다.
- 재귀적 타입 한정(Recursive type bound)
자기 자신이 들어간 표현식을 사용하여 타입 매개변수의 허용 범위를 한정할 수 있다.
public static <E extends Comparable<E>> E max(Collection<E> c) {...}
- 제네릭 타입과 마찬가지로, 매개변수와 반환값을 형변환 해야한다면 제네릭 메서드가 더 안전하고 쉽다.
아이템 31. 한정적 와일드카드를 사용해 API 유연성을 높이라
- 유연성을 높이기 위해 원소의 생산자나 소비자용 입력 매개변수에 와일드카드 타입을 사용하라.
- PECS : produce-extends, consumer-super 를 꼭 기억하자
- 반환 타입에는 한정적 와일드카드 타입을 사용하면 안돼!
Comparable, Comparator는 모두 소비자다.
클래스 사용자가 와일드카드 타입 신경쓰게되면 뭔가 잘못된 것임
- 비한정적 타입 매개변수(
List<E>
) vs 비한정적 와일드카드(List<?>
)
메서드 선언에 타입 매개변수가 한 번만 나오면 와일드카드로 대체하라
근데 List<?>
에는 null만 넣을 수 있음; private 도우미 메서드를 사용해야한다.
아이템 32. 제네릭과 가변인수를 함께 쓸 때는 신중하라
- 가변인수 메서드를 호출하면 가변인수를 담기 위해 배열이 생성되기 때문에, 실체화 불가 타입인 제네릭, 매개변수화 타입이 포함되면 안전하지 않다.
- 제네릭 배열을 직접 만드는 것은 허용하지 않음. 제네릭 varargs 매개변수를 받는 메서드는 선언할 수 있음 → 개발 편의성을 위해!
@SafeVaragrs
- 메서드 작성자가 해당 메서드가 타입 안전함을 보장하는 장치
@SafeVaragrs
는 메서드가 안전할 때만 쓰자
제네릭 varargs 매개변수 배열에 다른 메서드가 접근하도록 허용하면 안전하지 않다.
재정의할 수 없는 메서드에만 달아야 한다. (그래서 정적 메서드, final 인스턴스 메서드, private 인스턴스 메서드에만 허용)
아이템 33. 타입 안전 이종 컨테이너를 고려하라
ThreadLocal<T>
, AtomicReference<T>
같은 것은 단일원소 컨테이너임. 매개변수화되는 대상이 원소가 아닌 자기 자신이다.
- 더 유연한 타입 안전 이종 컨테이너!
컨테이너 대신 키를 매개변수화, 컨테이너에 값을 넣,뺄 때 매개변수화한 키를 같이 제공하면 된다.
→ 제네릭 시스템이 '값 타입 == 키 타입'을 보장해준다.
- 타입 토큰
컴파일, 런타임 타입 정보를 알기 위해 메서드들이 주고 받는 class 리터럴 (ex. String.class
, Integer.class
..)
List<String>.class
처럼 실체화 불가 타입에는 못 쓴다.
- 슈퍼 타입 토큰
List<String>.class
처럼 실체화 불가 타입도 클래스 리터럴 사용할 수 있게 해주는 묘수
ex. jackson의 TypeReference