Intro::
이펙티브 자바 정리본입니다.
결론
•
로 타입을 사용하면 런타임에 예외가 일어날 수 있습니다. 그러니 되도록이면 사용하지 말아야 합니다.
•
로 타입은 제네릭이 도입되기 이전 코드와의 호환성을 의해 제공될 뿐입니다.
•
예시로 Set<Object>와 Set<?>은 안전하지만, 이들의 로 타입인 Set은 안전하지 않습니다.
로 타입이란?
클래스와 인터페이스 선언에 타입 매개변수가 쓰이면, 이를 제네릭 클래스 혹은 제네릭 인터페이스라고 하며, 이를 통틀어서 제네릭 타입이라고 합니다.
// 원소 타입이 String인 리스트를 뜻하는 매개변수화 타입
// String 이 정규 타입 매개변수 E에 해당하는 실제 타입 매개변수입니다.
List<String> list ....
Java
복사
로 타입이란 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않을 때를 말합니다. 로 타입은 타입 선언에서 제네릭 타입 정보가 전부 지워진 것처럼 동작하는데, 제네릭이 도래하기 전 코드와 호환되도록 하기 위한 궁여지책입니다.
로 타입을 쓰면 제네릭이 안겨주는 안전성과 표현력을 모두 잃게 됩니다.
그럼 로타입은 뭐에 쓰라는 걸까??
•
기존 코드를 모두 수용하면서(제네릭을 사용하기 전) 제네릭을 사용하는 새로운 코드와도 호환되기 위해 사용합니다.
List<Object>처럼 임의 객체를 허용하는 매개변수화 타입에서의 로 타입은 사용해도 괜찮다.
로 타입인 List와 매개변수화 타입인 List<Object>의 차이는 다음과 같다.
List | List<Object> |
제네릭 타입에서 완전 발을 뺀 상태 | 모든 타입을 허용한다는 의사를 컴파일러에 명확히 전달 |
List<Object> 같은 매개변수화 타입을 사용할 때와 달리 List 같은 로 타입을 사용하면 타입 안전성을 읽게 됩니다.
// 코드 26-4 런타임에 실패한다. - unsafeAdd 메서드가 로 타입(List)을 사용 (156-157쪽)
public class Raw {
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
unsafeAdd(strings, Integer.valueOf(42));
String s = strings.get(0); // 컴파일러가 자동으로 형변환 코드를 넣어준다.
}
/**
* 런타임 단에서 에러 발생
* @param list
* @param o
*/
private static void unsafeRawAdd(List list, Object o) {
list.add(o);
}
/**
* 컴파일러단에서 에러 발생
* @param list
* @param o
*/
private static void unsafeAdd(List<Object> list, Object o) {
list.add(o);
}
}
Java
복사
원소의 타입을 몰라도 되는 경우
// 로 타입을 사용하는 경우
static int numElementsInCommon(Set s1, Set s2) {
int result = 0;
for (Object o : s1) {
if (s2.contains(o)) {
result++;
}
}
return result;
}
Java
복사
로타입을 사용하면 동작은 하지만 안전하지 않습니다. 따라서 비한정적 와일드카드 타입을 대신 사용하는 게 좋습니다.
// 비한정적 와일드카드 타입을 사용하는 경우
static int numElementsInCommon(Set<?> s1, Set<?> s2) {
int result = 0;
for (Object o : s1) {
if (s2.contains(o)) {
result++;
}
}
return result;
}
Java
복사
비한정적 와일드 카드 타입 특징
•
어떤 원소도(null 이외에는) 넣을 수 없습니다. 다른 원소를 넣으려 하면 컴파일 에러가 발생합니다.
로 타입을 사용하는 예외 케이스
•
class 리터럴
// 허용됨
List.class
String[].class
int.class
// 허용되지 않음
List<String>.class
List<?>.class
Java
복사
•
instanceof 연산자
◦
런타임에는 제네릭 타입 정보가 지워지므로 instanceof 연산자는 비한정적 와일드카드 타입 이외의 매개변수화 타입에는 적용할 수 없습니다.
if (o instanceof Set) {
Set<?> s = (Set<?>) o;
...
}
Java
복사
References::
이펙티브 자바 / 조슈아 블로크 지음 (프로그래밍 인사이트)