- 로 타입은 제네릭 타입에서 타입 매개변수를 사용하지 않은 것이다.
- 로 타입은 제네릭 타입이 제공하는 타입 안전성과 표현력을 잃는다
- 모든 타입을 사용하고 싶다면 Object 타입 매개변수로 컴파일러에 명확히 의사를 전하라
- 제네릭 타입(generic type) : 제네릭 클래스 + 인터페이스 ex) List<E>
- 매개변수화 타입(parameterized Type) : 매개변수 타입에 실제 타입변수가 정해진 type ex) List
- 실제 타입 매개변수(actual type parameter) : 대입된 실제 타입 ex) String
제네릭 타입에서 타입 매개변수를 사용하지 않은 것 ex) List
- raw Type은 제네릭을 사용하지 않는 타입이다
- 즉, 제네릭이 주는 장점을 활용하지 못한다
- 따라서 raw Type을 사용하면 안되는 이유 == 제네릭이 주는 장점
public class GenericBaisic {
public static void main(String[] args){
// Generic 사용하기 전
// 이 리스트에는 정수형만 들어가야 합니다
List numbers=new ArrayList();
numbers.add(10); // 정수형 원소 삽입
numbers.add("coli"); // 문자형 원소 삽입
for(Object number:numbers){
System.out.println((Integer)number);
}
}
}
- 표현력 : numbers에 정수형만 들어가야 한다고 주석을 통해 알림
- 타입 안전성
- 아무 오류 없이 컴파일됨
- 런타임에 ClassCastException 발생 => 빠른 fail이 불가
public class GenericBaisic {
public static void main(String[] args) {
// Generic 등장 이후
List<Integer> nuberms = new ArrayList<>();
nuberms.add(10);
nuberms.add("coli"); // 컴파일 에러 발생!!
for (Integer number: nuberms) {
System.out.println(number);
}
}
}
- 표현력 : 타입 선언 자체에 정수형 리스트임이 녹아듦
- 타입 안전성 : 컴파일 시점에서 오류 발생!
=> raw type은 제네릭이 안겨주는 표현력과 타입 안전성을 잃게 된다!
- 모든 타입을 허용해야 한다면 List를 사용해도 되지 않을까?
=> List<Object>를 사용해라!
List<Object>와 List의 차이는 무엇일까?
- List<Object>는 컴파일러에게 모든 타입 허용 의사를 명확히 전달한다
- 그러나, raw Type인 List는 타입 안전성을 완전히 잃는다
예시로 이해하는 List<Object>의 상대적 우위
public class Raw {
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
unsafeAdd(strings, Integer.valueOf(42));
String s = strings.get(0); // 컴파일러가 자동으로 형변환 코드를 넣어준다.
}
private static void unsafeAdd(List list, Object o) {
list.add(o);
}
}
- strings는 문자열 배열이다.
- 로 타입을 매개변수로 받는 unsafeAdd의 경우, 아무 타입이나 컬렉션에 삽입가능하다
- 런타임에
String s = strings.get(0);
코드에서 42가 나와 ClassCastException이 발생한다
그러나, 매개변수 타입을 List로 바꾸면? => 컴파일 오류
private static void unsafeAdd(List<Object> list, Object o) {
list.add(o);
}
- List은 List의 하위 타입이다.
- 그러나, List<Object>와 List<String>는 엄연히 다른 타입이다(아이템 28)
=> 따라서, 컴파일 타임에 오류가 발생한다!
다음은 공통 원소를 반환하는 메서드이다
static int numElementsInCommon(Set s1, Set s2) {
int result = 0;
for (Object o1 : s1) {
if (s2.contains(o1)) {
result++;
}
}
return result;
}
- 이 메서드는 동작하지만 안전하지 않다
- 로타입으로 들어온 Set안에 아무 객체나 넣을 수 있다
해결방안 : 비한정적 와일드 카드 타입
static int numElementsInCommon(Set s1, Set s2) {...};
- Collection<?>에는 null외에 어떤 원소도 넣을 수 없다
- 다른 원소를 넣으려 하면 오류 메시지를 보낼 것이다
- 자바는 class 리터럴에 매개변수화 타입을 사용하지 못하게 했다
- List<String>.class 같은 타입은 없다
- 런타임에는 제네릭 타입 정보가 지워진다(소거법)
- 정보가 지워지므로 instanceOf 연산자에서 제네릭 타입을 사용하는 이유는 없다
if(o instanceOf Set<String>) // 컴파일 타임
if(o instanceOf Set) // 런 타임
- generic이 나오기 이전 버전 코드와의 호환성을 위해
한글 용어 | 영문 용어 | 예시 |
---|---|---|
매개변수화 타입 | parameterized type | List < String > |
실제 타입 매개변수 | actual type parameter | String |
제네릭 타입 | generic type | List<E> |
정규 타입 매개변수 | formal type parameter | E |
비한정적 와일드카드 타입 | unbounded wildcard type | List<?> |
로 타입 | raw type | List |
한정적 타입 매개변수 | bounded type parameter | <E extends Number> |
재귀적 타입 한정 | recursive type bound | <T extends Comparable<T>> |
한정적 와일드카드 타입 | bounded wildcard type | List<? extends Number> |
제네릭 메서드 | generic method | static <E> List<E> as List(E[] a) |
타입 토큰 | type token | String.class |