제네릭 타입을 하나 정의하면 그에 딸린 로 타입(raw type)도 함께 정의된다. 로 타입이란 제네릭 타입에서 매개변수를 전혀 사용하지 않을 때를 말한다.
List<E>의 raw type은 List 다
로 타입은 타입 선언에서 제네릭 타입 정보가 전부 지워진 것처럼 동작하는데, 제네릭이 도래하기 전 코드와 호환되기로한 궁여지책이라 할 수있다.
// Stamp인스턴스만 취급한다.
private final Collection stamps = ...;
// 실수로 동전을 넣어도 동작한다
stamps.add(new Coin(...)); // unchecked call 경고를 내뱉는다.
위의 코드는 실수로 다른 형태의 인스턴스를 집어넣더라도 오류없이 컴파일 되면서 동작한다.
런타임에 가서야 뭔가 잘못 되었음을 알 수 있다.
private final Collection<Stamp> stamps = ...;
위와 같이 선언하면 stamps에는 Stamp의 인스턴스만 넣어야 함을 컴파일러에 인지시킬 수 있다.
또한 컴파일러는 컬렉션에서 원소를 꺼내는 모든 곳에 보이지 않는 형변환을 추가하여 절대 실패하지 않음을 보장한다.
이렇듯 로 타입을 쓰는것을 언어 차원에서 막아 놓지는 않았지만 절대로 써서는 안된다. 로 타이븡ㄹ 쓰면 제네릭이 안겨주는 안정성과 표현력을 모두 잃게 된다 그렇다면 쓰면 안되는 로타입을 왜 만들어 놓은 것일까? 바로 호환성 때문이다. 자바가 제네릭을 받아들이기까지 거의 10년이 걸린 탓에 제네릭 없이 짠 코드가 이미 세상을 뒤덮어 버렸다. 그래서 기존 코드와 함께 제네릭을 사용하기 위해서 로 타입이 필수 불가결했을 뿐이다.
원소의 타입을 몰라도 되는 로 타입을 쓰고 싶을때가 있을 수는 있다. 예를 들어 2개의 집합을 받아 공통 원소를 반환하는 메서드를 작성한다고 해보자.
static int numElementsInCommon(Set s1, Set s2) {
// ... 코드 생략
}
이 메서드는 동작하지만 로 타입 사용으로 인해 안전하지 않다. 대신 비한정적 와일드카드 타입을 대신 사용하는 게 좋다. 제네릭을 쓰고 싶지만 실제 타입 매개변수가 무엇인지 신경 쓰고 싶지 않다면 ?를 사용하자. 예컨대 제네릭 타입인 Set<E>
의 비한정적 와일드카드 타입은 Set<?>
이다. 이것이 어떤 타입이라도 담을 수 있는 가장 범용적인 매개변수화 Set 타입이다.
static int numElementsInCommon(Set<?> s1, Set<?> s2) { ... }
비한정적 와일드카드 타입은 로 타입에 비해서 훨씬 안전하다. 로 타입 컬렉션에는 아무 원소나 넣을 수 있으니 타입 불변식을 훼손하기 쉽다. 반면, Collection<?>
에는 Null이외에는 어떤 원소도 넣을 수 없다.다른 원소를 넣으려고 한다면 컴파일러로부터 오류메세지를 보게 될 것이다 이처럼 컴파일 단계에서 오류가 날 수 있는 가능성을 예방할 수 있다
로 타입을 사용하면 런타임에 문제가 생길 수 있으니 사용해서는 안된다. 로 타입은 제네릭 이전의 호환성을 보장하기 위해서 존재할 뿐이다. 만약 매개변수의 타입에 신경쓰고 싶지 않다면 비한정적 와일드카드 타입을 사용하자