HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
📖
공부한 책
/
📒
Effective Java
/
아이템 26 : 로 타입은 사용하지 말라

아이템 26 : 로 타입은 사용하지 말라

로 타입을 사용한 좋지않은 예제List 같은 로 타입은 x, List<Object> 매개변수화 타입은 괜찮다제네릭 타입을 쓰고 싶지만 실제 타입 매개변수가 무엇인지 신경 쓰고 싶지 않을 때 → 비한정적 와일드카드 타입 ( ? ) 를 사용하자로 타입 쓰지 말라는 규칙의 소소한 예외class 리터럴에는 로 타입을 써야 한다.instanceof 연산자에서는 로 타입, 비한정적 와일드카드 타입 똑같이 작용됨
💡
로 타입을 사용하면 런타임에 예외가 일어날 수 있으니 사용하면 안 된다. 로 타입은 제네릭이 도입되기 이전 코드와의 호환성을 위해 제공될 뿐이다. 빠르게 훑어보자면, Set<Object>는 어떤 타입의 객체도 저장할 수 있는 매개변수화 타입이고, Set<?>는 모종의 타입 객체만 저장할 수 있는 와일드카드 타입이다. 그리고 이들의 로 타입인 Set은 제네릭 타입 시스템에 속하지 않는다. Set<Object>와 Set<?>는 안전하지만, 로 타입인 Set은 안전하지 않다.

로 타입을 사용한 좋지않은 예제

private final Collection stamps = ...; stamps.add(new Coin(...));
실수로 도장 대신 동전을 넣어도 아무 오류 없이 컴파일되고 실행됨
  • 책 전반에서 이야기 하듯, 오류는 가능한 한 발생 즉시, 이상적으로는 컴파일할 때 발견하는 것이 좋다.
  • 위의 예에서는 오류가 발생하고 한참 뒤인 런타임에야 알아챌 수가 있음.
private final Collection<Stamp> stamps = ...;
  • 위와 같이 선언하면 컴파일러에서 인지하여 의도대로 동작하게 됨
로 타입을 쓰면 제네릭이 안겨주는 안전성과 표현력을 모두 잃게 된다. (호환성 때문에 로 타입이 있는 것임)

List 같은 로 타입은 x, List<Object> 매개변수화 타입은 괜찮다

  • 차이점
    • List는 제네릭 타입에서 완전히 발을 뺀 것이고
    • List<Object>는 모든 타입을 허용한다는 의사를 컴파일러에 명확히 전달한 것임
    • 매개변수로 List를 받는 메서드에 List<String>을 넘길 수 있지만, List<Object>를 받는 메서드에는 넘길 수 없다. ( 제네릭의 하위 타입 규칙 때문)
      • List<String>은 List의 하위 타입이지만, List<Object>의 하위타입은 아니기에 (아이템 28)
  • List<Object> 같은 매개변수화 타입을 사용할 때와 달리 List 같은 로 타입을 사용하면 타입 안전성을 잃게 됨

제네릭 타입을 쓰고 싶지만 실제 타입 매개변수가 무엇인지 신경 쓰고 싶지 않을 때 → 비한정적 와일드카드 타입 ( ? ) 를 사용하자

  • Set<E> → Set<?>
  • 차이점
    • 특징을 간단히 말하면 와일드카드 타입은 안전하고, 로 타입은 안전하지 않다.
    • 로 타입 컬렉션에는 아무 원소나 넣을 수 있으니 타입 불변식을 훼손하기 쉽다
    • 반면 Collection<?>에는 (null 외에는) 어떤 원소도 넣을 수 없다. & 컬렉션에서 꺼낼 수 있는 객체의 타입도 전혀 알 수 없게 함
위에서 설명한 바와 같이 ?를 이용했을때, Object도 못 넣고 String도 못넣음. null은 넣을 수 있게 됨
위에서 설명한 바와 같이 ?를 이용했을때, Object도 못 넣고 String도 못넣음. null은 넣을 수 있게 됨

로 타입 쓰지 말라는 규칙의 소소한 예외

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

  • 자바 명세는 class 리터럴에 매개변수화 타입을 사용하지 못하게 했다(배열과 기본 타입은 허용함)
    • 예를 들어 List.class, String[].class, int.class는 허용하고 List<String>.class와 List<?>.class는 허용하지 않음

instanceof 연산자에서는 로 타입, 비한정적 와일드카드 타입 똑같이 작용됨

  • 런타임에는 제네릭 타입 정보가 지워지므로 instanceof 연산자는 비한정적 와일드카드 타입 이외의 매개변수화 타입에는 적용할 수 없다.
  • 로 타입이든 비한정적 와일드카드 타입이든 instanceof는 완전히 똑같이 동작함
if (o instanceof Set) { // 로타입 Set<?> s = (Set<?>) o; // 와일드카드 타입 ... }
o의 타입이 Set임을 확인한 다음 와일드카드 타입인 Set<?>로 형변환해야 한다. 이는 검사 형변환(checked cast) 이므로 컴파일러 경고가 뜨지 않는다.