Home [Effective Java] Item26. 로 타입은 사용하지 말라!
Post
Cancel

[Effective Java] Item26. 로 타입은 사용하지 말라!

Item26. 로 타입은 사용하지 말라!

용어 정리

  • 제네릭 클래스(제네릭 인터페이스) 클래스와 인터페이스 선언에 타입 매개변수가 쓰이는 경우 제네릭 클래스 혹은 인터페이스라 칭한다.
  • 제네릭 타입 제네릭 클래스와 제네릭 인터페이스를 통틀어 제네릭 타입이라 칭한다.

  • 매개변수화 타입 제네릭 타입은 일련의 매개변수화 타입을 정의한다. 클래스(인터페이스) 이름이 나오고, 이어서 꺽쇠괄호 안에 실제 타입 매개변수를 나열한다.
  • 로 타입 제네릭 타입을 하나 정의하면 그에 딸린 로 타입도 함께 정의된다. 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않을 때를 말한다.

로 타입 (Raw Type)

로 타입이 뭔가?

  • 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않을 때를 말함.
  • 예를 들어 List<E>의 로 타입은 List다.

왜 사용하는가?

  • 제네릭이 도래하기 전 코드와 호환되도록 하기 위한 궁여지책이다.
    • 로 타입은 제네릭 타입 정보가 전부 지워진 것처럼 동작하니 호환 가능하긴 함.

예시

로 타입

1
2
3
4
5
// Stamp 인스턴스만 취급한다.
private final Collection stamps = ...;

// 실수로 Stamp가 아닌 동전을 넣는다.
stamps.add(new Coin(...)) // "unchecked call" 경고를 내뱉는다.
  • 아무 문제 없이 컴파일 및 실행된다.
  • 컬렉션에서 이 동전을 다시 꺼내기 전까지는 오류를 찾지 못한다.

제네릭 타입

1
2
private final Collection<Stamp> stamps = ...;
stamps.add(new Coin(...)) // 컴파일 에러
  • stamps에는 Stamp의 인스턴스만 넣어야 함을 컴파일러가 인지하게 된다.
  • 이제 실수로 동전을 넣으려 시도한다면 컴파일 오류가 발생하며 잘못된 점을 알려준다.

로 타입을 사용하지 말아야 하는 이유

  • 로 타입을 사용하면 제네릭이 안겨주는 안전성과 표현력을 모두 잃게 된다.

로 타입의 역할

  • 호환성
    • 자바가 제네릭을 받아들이기까지 걸린 시간 : 거의 10년
      • 이미 제네릭 없이 짠 코드가 세상을 뒤덮어 버리게 되었다.
    • 기존 코드를 모두 수용하면서 제네릭을 사용할 수 있어야 했다.
      • 이는 로 타입을 사용하는 메서드에 매개변수화 타입 인스턴스를 넘겨도 동작해야만 했던 것이다.(반대 상황도 동일)
    • 마이그레이션 호환성을 위해 로 타입을 지원하고, 제네릭 구현에 소거 방식을 사용.

매개변수화 타입

  • List 같은 로 타입은 안되지만, List<Object>처럼 임의 객체를 허용하는 매개변수화 타입은 사용해도 괜찮다.

  • 제네릭 타입에서 완전히 발을 뺀 로 타입과는 달리, List<Obejct>는 모든 타입을 허용한다는 의도를 컴파일러에 명확히 전달하고 있다.
  • List<Object>List의 하위 타입이 아니다.
    • List<Object> 같은 매개변수화 타입 사용 때와는 달리 List 같은 로 타입 사용 시, 타입 안전성을 잃게 된다.

와일드 카드 타입

  • 제네릭 타입을 쓰고 싶지만 실제 타입 매개변수가 무엇인지 신경쓰고 싶지 않을 수 있다.
    • 이런 경우, 물음표(?)를 사용하자. ?를 통해 비한정적 와일드카드 타입을 만들 수 있다.
    1
    
    static int numElementsInCommon(Set<?> set1, Set<?> set2) {....}
    
  • 와일드 카드 타입은 안전하고, 로 타입은 안전하지 않다.
    • 로 타입은 아무 원소나 대입 가능해 타입 불변식을 훼손하기 쉽다.
    • 반면 와일드 카드 타입인 Collection<?>에는 null 외에는 어떤 원소도 넣을 수 없다.
      • 비한정적 와일드카드 타입이다. 애초에 실제 타입을 대입하려는 것이 아닌 로 타입 대체 역할이기에 제약이 있다.
      • 비한정적 와일드카드 타입의 살제 타입을 고려하지 않는 제약에서 벗어나려면 한정적 와일드카드 타입을 사용하면 된다.

예외

  • class 리터럴에는 로 타입을 써야 한다.

    • 자바 명세는 class 리터럴에 매개변수화 타입을 사용하지 못하게 했다.
  • instanceof 연산자

    • 런타임에는 제네릭 타입 정보가 지워지므로 instanceof 연산자는 비한정적 와일드카드 타입 이외의 매개변수화 타입에는 적용이 불가하다.
    • 즉, 이 경우 꺽쇠괄호 부분은 코드만 지저분해지게 하는 요소이니, 제거해 로 타입을 사용한다.
    1
    2
    3
    
    if (o instanceof Set) {      // 로 타입
      	Set<?> s = (Set<?>) o; 	 // 와일드카드 타입
    }
    

핵심 정리

  • 로 타입을 사용하면 런타임에 예외가 일어날 수 있으니 사용하면 안 된다.
  • 로 타입은 제네릭이 도입되기 이전 코드화의 호환성을 위해 제공될 뿐이다.
  • Set<Object> : 어떤 타입의 객체도 저장 가능한 매개변수화 타입 (안전)
  • Set<?> : 모종의 타입 객체만 저장할 수 있는 와일드카드 타입 (안전)
  • Set : 로 타입, 제네릭 타입 시스템에 속하지 않는다. (불안전)
This post is licensed under younghwani by the author.

[Effective Java] Item25. 톱레벨 클래스는 한 파일에 하나만 담으라!

[Effective Java] Item27. 비검사 경고를 제거하라!

Comments powered by Disqus.