IT/Programming / / 2023. 4. 27. 09:29

<Effective Java> RULE 23 새 코드에는 무인자 제네릭 자료형을 사용하지 마라

반응형

 

자바 1.5이전 컬렉션

private final Collection stamps = ...;

// 엉뚱한 자료형을 넣어도 컴파일 시 문제가 없었다.
stamps.add(new Coin(...));

for(Iterator i = stamps.iterator() ; i.hasNext(); ){
    Stamp s = (Stamp) i.next(); //ClassCastException 예외 발생
    ...//우표 작업
}
 

ClassCastException이 발생하게 되면 stamps컬렉션에 엉뚱한 객체를 넣는 코드를 찾아야 한다.

// 형인자 컬렉션 자료형 - 형 안전성 확보
private final Collection<Stamp> stamps = ...;
 

위와 같이 선언하면 stamps 컬렉션에 Stamp 객체만 넣을 수 있다는 것을 이해할 수 있으며 실제로 Stamp 객체만 삽입되도록 한다.

게다가 원소를 꺼낼 때 형변환을 하지 않아도 된다.

- for-each던 for문이건 마찬가지 이다.

 

List와 같은 무인자 자료형을 사용하면 형 안전성을 잃게 되지만, List<Object>와 같은 형인자 자료형을 쓰면 그렇지 않다.

// 실행 도중에 오류를 일으키는 무인자 자료형 사용 예
public static void main(String[] args){
    List<String> strings = new ArrayList<String>();
    unsafeAdd(strings, new Integer(42));
    String s = strings.get(0); // 컴파일러가 자동으로 형 변환
}
private static void unsafeAdd(List list, Object o){
    list.add(o);
}

//// 결과 : 컴파일은 잘되지만 무인자 자료형 list를 썻기 때문에 경고가 뜬다.
waring : unchecked call to add(E) in raw type List
    list.add(o);
 

실제로 돌려보면 string.get(0)의 실행 결과를 String으로 변환하려 하기 때문에 ClassCastException예외가 발생한다.

 

버전 1.5부터 자바는 비한정적 와일드카드 자료형을 제공한다.

- 제네릭 자료형을 쓰고 싶으나 실제 형인자가 무엇인지는 모르거나 신경쓰고싶지 않을 때는 형인자로 ?를 쓰면 된다.

- Set<E>에 대한 비한정적 와일드 카드 자료형은 Set<?>이다.

//비한정적 와일드 카드 자료형 - 형 안전성과 유연성 만족
static int numElementsIncommon(Set<?> s1, Set<?> s2){
    int result = 0;
    for (Object o1 :s1){
        if(s2.contains(o1))
            result++;
    return result;
}
 

Set<?>과 Set의 차이는 무엇인가?

- 와일드 카드 자료형은 안전하지만 무인자 자료형은 그렇지 않다.

- 무인자 자료형은 아무 객체나 넣을 수 있어서 컬릭션 자료형 불변식이 깨진다.

- 그러나 Collection<?>에는 null이외에 어떤 원소도 넣을 수 없다.

ㄴ어떤 자료형의 객체를 담는 컬렉션인지 알 방법이 없기 때문이다.

 

새로운 코드에는 무인자 자료형을 쓰면 안된다고 했지만 예외가 두가지 있다.

1) 클래스 리터럴에는 반드시 무인자 자료형을 사용해야 한다.

- 자바 표준에 따르면 클래스 리터럴에는 형인자 자료형을 쓸 수 없다.

 배열 자료형이나 기본자료형은 쓸수 있다.

ㄴex) List.class, String[].class, int.class...

- instanceof 연산자는 비한정적 와일드 카드 자료형 이외의 형인자 자료형에 적용할 수 없다.

// instancof 연산자에는 무인자 자료형을 써도 OK
if( o instanceof Set ){     //무인자 자료형
    Set<?> m = (Set<?>) o;  //와일드 카드 자료형
    ....
}
 

* o가 Set객체라는것이 확실해진 다음에는 와일드 카드 자료형 Set<?>로 형변환 해야하는 것에 주의하자.

 

요약

- 무인자 자료형을 쓰면 프로그램 실행 도중에 예외가 발생할 수 있으므로, 새로 만드는 코드에는 무인자 자료형을 쓰지 말아야 한다.

- 무인자 자료형을 제네릭 도입 전 코드 호환성을 유지하기 위해 제공하는것에 불과하다.

- Set<Object> 는 어떤 자료형의 객체도 담을 수 있는 집합의 자료형

- Set<?>는 모종의 자료형 객체만 담을 수 있는 집합의 자료형

- Set<Object>와 Set<?>는 안전하지만 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

 

 

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유