클래스와 인터페이스 선언에 타입 매개변수가 쓰이면 이를 제네릭 클래스, 제네릭 인터페이스라 한다.
예를 들어 List 인터페이스는 원소의 타입을 나타내는 타입 매개변수 E를 받는다.
그래서 이 인터페이스의 완전한 이름은 List<E>지만 짧게 그냥 List라고 쓴다.
제네릭 타입을 하나 정의하면 그에 딸린 로 타입(raw type)도 함께 정의된다.
로 타입이란 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않을 때를 말한다.
예를 들어 List<E>의 로 타입은 List다.
로 타입은 타입 선언에서 제네릭 타입 정보가 전부 지워진 것처럼 동작하는데, 제네릭이 나오기 전 코드와 호환되도록 하기 위한 궁여지책이라 할 수 있다.
오류는 가능한 발생 즉시, 이상적으로는 컴파일할 때 발견하는 것이 좋다.
예를 들어 List 로 타입 사용하면 해당 타입을 넣을 때는 문제가 없으나 꺼낼 때 문제가 생긴다.
로 타입을 쓰는걸 언어 차원에서 막아놓지는 않았지만 절대로 써서는 안된다.
로 타입을 쓰면 제네릭이 안겨주는 안전성과 표현력을 모두 잃게 된다.
그렇다면 절대 써서는 안되는 로 타입을 애초에 왜 만들어 놓은걸까?
위에서 말했듯이 호환성 때문이다.
자바가 제네릭을 받아들이기까지 거의 10년이 걸린 탓에 제네릭 없이 짠 코드가 이미 세상을 뒤덮어 버렸다.
List 같은 로 타입은 사용해서는 안되지만 List<Object>처럼 임의 객체를 허용하는 매개변수화 타입은 괜찮다.
List와 List<Object>의 차이는 무엇일까?
List는 제네릭 타입에서 완전히 발을 뺀 것이고, List<Object>는 모든 타입을 허용한다는 의사를 컴파일러에 명확히 전달한 것이다.
매개변수로 List를 받는 메서드에 List<String>을 넘길 수 있지만 List<Object>를 받는 메서드에는 넘길 수 없다.
이는 제네릭의 하위 타입 규칙이기 때문이다.
List<String>은 로 타입인 List의 하위 타입이지만 List<Object>의 하위 타입은 아니다.
그 결과 List<Object> 같은 매개변수화 타입을 사용할 때와 달리 List 같은 로 타입을 사용하면 안정성을 잃게 된다.
제네릭 타입을 쓰고 싶지만 실제 타입 매개변수가 무엇인지 신경쓰고 싶지 않다면 물음표(?)를 사용하자.
비한정적 와일드카드 타입인 Set<?>와 Set의 차이는 무엇일까?
이것도 마찬가지로 Set<?>은 안전하고 Set은 안전하지 않다.
로 타입 컬렉션은 아무 원소나 넣을 수 있으니 타입 불변식을 훼손하기 쉽다.
반면 Collection<?>에는 (null 외에는) 어떤 원소도 넣을 수 없다.
이러한 제약을 받아들일 수 없다면 제네릭 메서드(아이템 30)나 한정적 와일드 타입(아이템 31)을 사용하면 된다.
로 타입을 쓰지 말라는 규칙에도 소소한 예외가 몇 개 있다.
1. class 리터럴에는 로 타입을 써야한다.
List.class, String[].class, int.class는 허용하고 List<String>.class, List<?>.class는 허용하지 않는다.
2. instanceof 연산자를 사용할 때는 로타입을 써야한다.
런타임에는 제네릭 타입 정보가 지워지기 때문이다.
핵심 정리
로 타입을 사용하면 컴파일 예외가 일어날 수 있으니 사용하면 안 된다.
로 타입은 단지 이전 코드와의 호환성을 위해 제공될 뿐이다.
Set<Object>는 어떤 타입의 객체도 저장할 수 있는 매개변수화 타입이고, Set<?>는 모종의 타입 객체만 저장할 수 있는 와일드 카드 타입이다.
아래에 이번 장과 다음 장에 자주 나오는 용어들을 정리한다.
매개변수화 타입 - List<String>
실제 타입 매개변수 - String
제네릭 타입 - List<E>
정규 타입 매개변수 - E
비한정적 와일드카드 타입 - List<?>
로 타입 - List
한정적 타입 매개변수 - <E extends Number>
재귀적 타입 한정 - <T extends Comparable<T>>
한정적 와일드카드 타입 - List<? extends Number>
제네릭 메서드 - static <E> List<E> asList(E[] a)
타입 토큰 - String.class
'Book > Effective Java' 카테고리의 다른 글
[Effective Java]28.배열보다는 리스트를 사용하라 (0) | 2022.07.07 |
---|---|
[Effective Java]27.비검사 경고를 제거하라 (0) | 2022.07.07 |
[Effective Java]25.톱레벨 클래스는 한 파일에 하나만 담으라 (0) | 2022.07.06 |
[Effective Java]24.멤버 클래스는 되도록 static으로 만들라 (0) | 2022.07.06 |
[Effective Java]23.태그 달린 클래스보다 클래스 계층구조를 활용하라 (0) | 2022.07.06 |