Skip to content

Latest commit

 

History

History
184 lines (141 loc) · 6.34 KB

로_타입은_사용하지_말라.md

File metadata and controls

184 lines (141 loc) · 6.34 KB

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

핵심정리

  • 로 타입은 제네릭 타입에서 타입 매개변수를 사용하지 않은 것이다.
  • 로 타입은 제네릭 타입이 제공하는 타입 안전성과 표현력을 잃는다
  • 모든 타입을 사용하고 싶다면 Object 타입 매개변수로 컴파일러에 명확히 의사를 전하라

1. raw Type이란?

제네릭 용어 정리

  • 제네릭 타입(generic type) : 제네릭 클래스 + 인터페이스 ex) List<E>
  • 매개변수화 타입(parameterized Type) : 매개변수 타입에 실제 타입변수가 정해진 type ex) List
  • 실제 타입 매개변수(actual type parameter) : 대입된 실제 타입 ex) String

raw Type이란?

제네릭 타입에서 타입 매개변수를 사용하지 않은 것 ex) List


2. raw Type을 사용하면 안되는 이유

  • raw Type은 제네릭을 사용하지 않는 타입이다
  • 즉, 제네릭이 주는 장점을 활용하지 못한다
  • 따라서 raw Type을 사용하면 안되는 이유 == 제네릭이 주는 장점

Generic : 타입 안전성 + 표현력

public class GenericBaisic {
    public static void main(String[] args){
        // Generic 사용하기 전
        // 이 리스트에는 정수형만 들어가야 합니다
        List numbers=new ArrayList();
        numbers.add(10); // 정수형 원소 삽입
        numbers.add("coli"); // 문자형 원소 삽입

        for(Object number:numbers){
            System.out.println((Integer)number);
        }
    }
}
  • 표현력 : numbers에 정수형만 들어가야 한다고 주석을 통해 알림
  • 타입 안전성
    • 아무 오류 없이 컴파일됨
    • 런타임에 ClassCastException 발생 => 빠른 fail이 불가
public class GenericBaisic {
    public static void main(String[] args) {
        // Generic 등장 이후
        List<Integer> nuberms = new ArrayList<>();
        nuberms.add(10);
        nuberms.add("coli"); // 컴파일 에러 발생!!

        for (Integer number: nuberms) {
            System.out.println(number);
        }
    }
}
  • 표현력 : 타입 선언 자체에 정수형 리스트임이 녹아듦
  • 타입 안전성 : 컴파일 시점에서 오류 발생!

=> raw type은 제네릭이 안겨주는 표현력과 타입 안전성을 잃게 된다!

3. 모든 타입을 허용할 때 : List<Object>

  • 모든 타입을 허용해야 한다면 List를 사용해도 되지 않을까?
    => List<Object>를 사용해라!

List<Object>와 List의 차이는 무엇일까?

  • List<Object>는 컴파일러에게 모든 타입 허용 의사를 명확히 전달한다
  • 그러나, raw Type인 List는 타입 안전성을 완전히 잃는다


예시로 이해하는 List<Object>의 상대적 우위

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);
    }
}
  • strings는 문자열 배열이다.
  • 로 타입을 매개변수로 받는 unsafeAdd의 경우, 아무 타입이나 컬렉션에 삽입가능하다
  • 런타임에 String s = strings.get(0); 코드에서 42가 나와 ClassCastException이 발생한다

그러나, 매개변수 타입을 List로 바꾸면? => 컴파일 오류

 private static void unsafeAdd(List<Object> list, Object o) {
        list.add(o);
}
  • List은 List의 하위 타입이다.
  • 그러나, List<Object>와 List<String>는 엄연히 다른 타입이다(아이템 28)

=> 따라서, 컴파일 타임에 오류가 발생한다!


4. 무슨 타입인지 모를 때 : 비한정적 와일드 카드 타입

다음은 공통 원소를 반환하는 메서드이다

static int numElementsInCommon(Set s1, Set s2) {
        int result = 0;
        for (Object o1 : s1) {
            if (s2.contains(o1)) {
                result++;
            }
        }

        return result;
    }
  • 이 메서드는 동작하지만 안전하지 않다
  • 로타입으로 들어온 Set안에 아무 객체나 넣을 수 있다

해결방안 : 비한정적 와일드 카드 타입
static int numElementsInCommon(Set s1, Set s2) {...};

  • Collection<?>에는 null외에 어떤 원소도 넣을 수 없다
  • 다른 원소를 넣으려 하면 오류 메시지를 보낼 것이다

5. 로타입을 사용 가능한 2가지

5-1. class 리터럴 타입

  • 자바는 class 리터럴에 매개변수화 타입을 사용하지 못하게 했다
  • List<String>.class 같은 타입은 없다

5-2. instanceOf 연산자

  • 런타임에는 제네릭 타입 정보가 지워진다(소거법)
  • 정보가 지워지므로 instanceOf 연산자에서 제네릭 타입을 사용하는 이유는 없다
if(o instanceOf Set<String>) // 컴파일 타임
if(o instanceOf Set) // 런 타임

6. 그럼에도 raw Type을 살려둔 이유

  • generic이 나오기 이전 버전 코드와의 호환성을 위해

5장 용어 정리

한글 용어영문 용어예시
매개변수화 타입parameterized typeList < String >
실제 타입 매개변수actual type parameterString
제네릭 타입generic typeList<E>
정규 타입 매개변수formal type parameterE
비한정적 와일드카드 타입unbounded wildcard typeList<?>
로 타입raw typeList
한정적 타입 매개변수bounded type parameter<E extends Number>
재귀적 타입 한정recursive type bound<T extends Comparable<T>>
한정적 와일드카드 타입bounded wildcard typeList<? extends Number>
제네릭 메서드generic methodstatic <E> List<E> as List(E[] a)
타입 토큰type tokenString.class