로 타입을 사용한 좋지않은 예제List 같은 로 타입은 x, List<Object> 매개변수화 타입은 괜찮다제네릭 타입을 쓰고 싶지만 실제 타입 매개변수가 무엇인지 신경 쓰고 싶지 않을 때 → 비한정적 와일드카드 타입 ( ? ) 를 사용하자로 타입 쓰지 말라는 규칙의 소소한 예외class 리터럴에는 로 타입을 써야 한다.instanceof 연산자에서는 로 타입, 비한정적 와일드카드 타입 똑같이 작용됨
로 타입을 사용하면 런타임에 예외가 일어날 수 있으니 사용하면 안 된다. 로 타입은 제네릭이 도입되기 이전 코드와의 호환성을 위해 제공될 뿐이다. 빠르게 훑어보자면, Set<Object>는 어떤 타입의 객체도 저장할 수 있는 매개변수화 타입이고, Set<?>는 모종의 타입 객체만 저장할 수 있는 와일드카드 타입이다. 그리고 이들의 로 타입인 Set은 제네릭 타입 시스템에 속하지 않는다. Set<Object>와 Set<?>는 안전하지만, 로 타입인 Set은 안전하지 않다.
로 타입을 사용한 좋지않은 예제
private final Collection stamps = ...; stamps.add(new Coin(...));
- 책 전반에서 이야기 하듯, 오류는 가능한 한 발생 즉시, 이상적으로는 컴파일할 때 발견하는 것이 좋다.
- 위의 예에서는 오류가 발생하고 한참 뒤인 런타임에야 알아챌 수가 있음.
private final Collection<Stamp> stamps = ...;
- 위와 같이 선언하면 컴파일러에서 인지하여 의도대로 동작하게 됨
로 타입을 쓰면 제네릭이 안겨주는 안전성과 표현력을 모두 잃게 된다. (호환성 때문에 로 타입이 있는 것임)
List 같은 로 타입은 x, List<Object> 매개변수화 타입은 괜찮다
- 차이점
- List는 제네릭 타입에서 완전히 발을 뺀 것이고
- List<Object>는 모든 타입을 허용한다는 의사를 컴파일러에 명확히 전달한 것임
- 매개변수로 List를 받는 메서드에 List<String>을 넘길 수 있지만, List<Object>를 받는 메서드에는 넘길 수 없다. ( 제네릭의 하위 타입 규칙 때문)
- List<String>은 List의 하위 타입이지만, List<Object>의 하위타입은 아니기에 (아이템 28)
- List<Object> 같은 매개변수화 타입을 사용할 때와 달리 List 같은 로 타입을 사용하면 타입 안전성을 잃게 됨
제네릭 타입을 쓰고 싶지만 실제 타입 매개변수가 무엇인지 신경 쓰고 싶지 않을 때 → 비한정적 와일드카드 타입 ( ? ) 를 사용하자
- Set<E> → Set<?>
- 차이점
- 특징을 간단히 말하면 와일드카드 타입은 안전하고, 로 타입은 안전하지 않다.
- 로 타입 컬렉션에는 아무 원소나 넣을 수 있으니 타입 불변식을 훼손하기 쉽다
- 반면 Collection<?>에는 (null 외에는) 어떤 원소도 넣을 수 없다. & 컬렉션에서 꺼낼 수 있는 객체의 타입도 전혀 알 수 없게 함

로 타입 쓰지 말라는 규칙의 소소한 예외
class 리터럴에는 로 타입을 써야 한다.
- 자바 명세는 class 리터럴에 매개변수화 타입을 사용하지 못하게 했다(배열과 기본 타입은 허용함)
- 예를 들어 List.class, String[].class, int.class는 허용하고 List<String>.class와 List<?>.class는 허용하지 않음
instanceof 연산자에서는 로 타입, 비한정적 와일드카드 타입 똑같이 작용됨
- 런타임에는 제네릭 타입 정보가 지워지므로 instanceof 연산자는 비한정적 와일드카드 타입 이외의 매개변수화 타입에는 적용할 수 없다.
- 로 타입이든 비한정적 와일드카드 타입이든 instanceof는 완전히 똑같이 동작함
if (o instanceof Set) { // 로타입 Set<?> s = (Set<?>) o; // 와일드카드 타입 ... }