Item26 - 로 타입은 사용하지 말라

2023. 12. 4. 23:13java/이펙티브 자바

 

- 런타임이 아닌 컴파일 타임에 문제를 찾을 수 있다.(안전성)

- 제네릭을 활용하면 이 정보가 주석이 아닌 타입 선언 자체에 녹아든다(표현력)

public static void main(String[] args) {
    //Generic 사용전
    List numbers = new ArrayList();
    numbers.add(10);
    numbers.add("son");

    for (Object number : numbers) {
        System.out.println((Integer) number); => 런타임 에러
    }

    //Generic 등장 후
    List<Integer> numberss = new ArrayList<>();
    numberss.add(10);
    numberss.add("son"); => 컴파일 에러

    for (Object number : numberss) {
        System.out.println((Integer) number);
    }

}

 

 

- 그렇다면 자바는 "로 타입"을 왜 지원하는가?

 => 제네릭이 없었던 이전 버번의 하위호환성 때문에

 

 - List 같은 로 타입은 사용해선 안 되나, List<Object>처럼 임의 객체를 허용하는 매개변수화 타입은 괜찮다. 둘의 차이점이 뭘까?

 => 매개변수로 List를 받는 메서드에 List<String>을 넘길 수 있지만, List<Object>를 매개변수로 받는 메서드에는 넘길 수 없다. 이는 제네릭의 하위 타입 규칙 때문인데 아이템 28에서 자세하게 배울 것이다.

 

 - 로 타입을 쓴 메서드에는 어떤 문제가 있을까?

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

    return result;
}

public static void main(String[] args) {
    Set<Integer> set = new HashSet<>();
    set.add(1);
    set.add(2);
    set.add(3);

    System.out.println(Numbers.numElementsInCommon(set, Set.of(1, 2)));
}

=> 이 메서드는 동작은 하지만 로 타입을 사용해 안전하지 않다. 불변식을 훼손하기 쉽기 때문이다. 예를 들면 numElementsInCommon 메서드의 첫 번재 매개변수 s1.add(1)을 하면 작동한다.

 

반면 Set에 비한정적 와일드카드 타입을 사용하면 s1.add(1)은 에러를 뱉는다.

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

    return result;
}