JAVA/Effective Java

제네릭 - 규칙 23 새 코드에는 무인자 제네릭 자료형을 사용하지 마라

반응형


1. 용어 정리
- List<E>와 같은 형식은 제네릭 클래스와 인터페이스로서 제네릭 자료형이라고 한다.
- 제네릭 자료형은 raw Type인 무인자 자료형을 같는다. List<E>의 raw type은 List이다.
- raw type을 통해 사용하면 추후에 element를 사용할 시 캐스팅을 잘 해주어야 한다. 그렇기에 형 안정성 확보를 위해 컬렉션에 담길 객체의 자료형이 무엇인지 알려준다.


2. raw type vs generic type


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 문제 1 (캐스팅 오류)
List a = new ArrayList();
a.add(generic);
        
for (Iterator i = a.iterator(); i.hasNext();) {
    (Dv) i.next(); // 잘못된 캐스팅으로 ClassCastException 발생
}
 
// 형 안정성 확보를 위해 raw type이 아닌 제네릭 자료형을 사용한다.
// 제네릭 자료형을 사용할 시 캐스팅도 필요없다.
List<Generic> a = new ArrayList<>();
 
// 문제 2 ( unsafeAdd ) 
// raw type을 사용할 경우에 list에 데이터가 서로 다른 자료 불변형 형태가 나타날 수 있다.
public static void main (String args[]) {
  List<String> a = new ArrayList<>();
  a.add("babo");
  setData(a, 12);
}
 
public static void setData(List list, int data) {
    list.add(data);
}
cs




위의 예를 보면 알지만 무인자 자료형을 사용할 시 
잘못된 객체를 집어넣어야 하거나 캐스팅 실수등이 있을 수 있기에 제네릭 자료형을 꼭 사용하는 것이 좋다.

그렇다면 왜 위험한 무인자 자료형 (raw type)을 자바는 아직도 지원을 하는가?
그 이유는 예전에 만들어진 코드들이 무인자 자료형으로 만들어져서 이를 위한 호환성 때문이다.

그래서 만약 꼭 무인자 자료형이 쓰고 싶다거나
컴파일러에게 어떠한 자료형도 들어갈 수 있다고 알려주기 위해서는 다음과 같이 Object를 Generic Type으로 지정해 주어야 한다.


1
2
3
List<Object> = new ArrayList<>();
 
public void setData(List<Object> list);
cs





3. raw type vs wild card


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// raw type
public static void printSet(Set<?> s) {
    s.add(10);//this line is illegal 
    for (Object o : s) {
        System.out.println(o);
    }
}
 
// wildcard
public static void printSet(Set s) {
    s.add("2");
    for (Object o : s) {
        System.out.println(o);
    }
}
cs



무인자 자료형을 사용할 경우
넘어오는 Set의 타입이 어떤 것인지 상관업이 어떤 형태의 데이터를 집어 넣을 수 있다. 

이점 때문에 오류가 발생한다. 

예를 들어 Set의 타입이 integer인 경우에 위의 코드에서는 String 객체를 집어넣으려고 하기 때문에 오류가 발생할 수 있다.

하지만 와일드 카드의 경우에는 null이외의 어떠한 값도 넣을 수 없기 때문에 무분별한 데이터 조작으로 인해 오류가 발생하는 것을 막을 수 있다.

또한 

무인자 자료형은 아무런 객체나 넣을 수 있어서, 
맨위의 예로 들었던 자료형 불변식이 쉽게 깨질 수 있다.

그렇기 때문에 와일드 카드는 안전하지만 무인자 자료형(raw type)은 안전하지 않다.



4. 무인자 자료형을 꼭 사용해야 하는 경우
1) 클래스 리터럴에는 반드시 무인자 자료형을 사용해야 한다.
=> 자바 표준에 따르면, 클래스 리터럴에는 형인자 자료형을 쓸 수 없다.



1
2
3
List.classString[].classint.class는 가능
 
List<?>.class, List<String>.class는 불가능
cs



2) instanceof는 와일드카드 방식과 무인자 자료형이 동일하므로 코드가 지저분해 보이지 않도록 무인자 자료형을 쓰도록 하자.


1
2
3
4
if ( data instanceof Set) {
// 하지만 주의 할 것은 set이 맞을 경우에는 다시 와일드카드로 캐스팅 해주어야 한다.
Set<?> m = (Set<?>) data;
}
cs


결론.

안전하게 사용하기 위해서는 예외사항을 제외하고는 무인자 자료형을 사용하지말자.

반응형