Skip to content

item 26 leekyunghee

leekyunghee edited this page Jul 31, 2020 · 7 revisions

Raw 타입(타입 매개변수가 없는 제네릭 타입)은 사용하지 말라

제네릭 클래스 혹은 제네릭 인터페이스란?

클래스와 인터페이스 선언에 타입 매개변수(type parameter)가 쓰일 경우

List 인터페이스는 원소의 타입을 나타내는 타입 매개변수 E를 받는다. 인터페이스의 완전한 이름은 List지만 짧게 그냥 List 라고도 자주쓴다.

  • 제네릭 타입은 일련의 매개변수화 타입을 정의한다. (클래스/인터페이스 이름 + <타입>)
  • Raw type이란 List의 Raw 타입은 List다.
  • Raw type은 타입 선언에서 제네릭 타입 정보가 전부 지워진 것처럼 동작하는데 제네릭이 도래하기 전 코드와 호환되도록 하기 위한 궁여지책이라 할 수 있다.
  • Raw type도 함께 정의된다.
[before] 컬렉션의 Raw 타입 - 따라하지 말 것!
private final Collection stamps = ...;

// 이 코드를 사용하면 실수로 도장(Stamp) 대신 동전(Coin)을 넣어도 아무 오류 없이 컴파일되고 실행된다.(컴파일러가 모호한 경고 메시지를 보여주긴 할 것이다.)
stamps.add(new Coin(...));  // "unchecked call" 경고를 내뱉는다. 

컬렉션에서 이 동전을 다시 꺼내기 전에는 오류를 알아채지 못한다. 

[before] 반복자의 로 타입 - 따라하지 말 것!
for (Iterator i = stamps.iterator(); i.hasNext();) {
    Stamp stamp = (Stamp) i.next();   // ClassCastException 을 던진다. 
    stamp.cancel();
}

[after] 매개변수화된 컬렉션 타입 - 타입 안전성 확보
private final Collection<Stamp> stamps = ...;

// 컴파일러가 인지하게 됨 
// 컴파일러는 컬렉션에서 원소를 꺼내는 모든 곳에 보이지 않는 형변환을 추가하여 절대 실패하지 않음을 보장한다.  

Test.java:9: error: incompatible types: Coin cannot be converted to Stamp
stamp.add(new Coin());

// Stamp용 컬렉션에 Coin을 넣는다는 예시
// 예를 들어 BigDecimal용 컬렉션에 BigInteger를 넣는 실수는 그리 억지 같지 않을 것
// 로타입(타입 매개변수가 없는 제네릭 타입)을 쓰는 걸 언어 차원에서 막아 놓지는 않았지만 절대로 써서는 안된다. 
// 로 타입을 쓰면 제네릭이 안겨주는 안전성과 표현력을 모두 잃게 된다. 

List같은 Raw 타입은 사용해서는 안되나 List 처럼 임의 객체를 허용하는 매개변수화 타입은

괜찮다.

  • Raw 타입인 List와 매개변수화 타입은 List의 차이

List는 제네릭 타입에서 완전히 발을 뺀 것이고 List는 모든 타입을 허용한다는 의사를 컴파일러에 명확히 전달

매개변수로 List를 받는 메서드에 List을 넘길 수 있지만 List를 받는 메서드에는 넘길 수 없다.

제네릭의 하위 타입 규칙

List은 Raw 타입인 List의 하위 타입이지만 List의 하위 타입은 아니다.

// 코드 26-4 런타임에 실패한다. - unsafeAdd 메서드가 Raw 타입(List)을 사용
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);     // [unchecked] call to add(E) as member of the raw type List
    } 
}

핵심 정리

Raw 타입을 사용하면 런타임에 예외가 일어날 수 있으니 사용하면 안된다. 
Set<Object>와 Set<?>은 안전하지만 로 타입인 Set은 안전하지 않다. 
Clone this wiki locally