로 타입은 사용하지 말라

김종준·2023년 5월 18일
0

이펙티브자바

목록 보기
22/63

로 타입은 사용하지 말라

로 타입이란 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않을 때를 말한다.

로 타입은 타입 선언에서 제네릭 타입 정보가 전부 지워진 것처럼 동작하는데, 제네릭이 도래하기 전 코드와 호환되도록 하기 위한 궁여지책이라 할 수 있다.

오류는 가능한 발생한 즉시, 이상적으로는 컴파일할 때 발견하는 것이 좋다.

하지만 로 타입을 사용하는 경우 오류가 발생하고 한참 뒤인 런타임에서야 알 수 있다.

로 타입을 사용하는 것을 언어 차원에서 막아 놓지는 않은 이유는 로 타입 사용하면 제네릭이 안겨주는 안전성과 표현력을 모두 잃게 되기 때문이다.

하지만 자바가 제네릭을 받아들이기까지 10년이 걸린 탓에 제네릭 없이 짠 코드가 이미 세상을 뒤덮었다.

로 타입을 사용하는 메서드에 매개변수화 타입의 인스턴스를 넘겨도 동작해야만 하는 것이다.

이 마이그레이션 호환성을 위해 로 타입을 지원하고 제네릭 구현에는 소거 방식을 사용하게 되었다.

List 같은 로 타입은 사용해서는 안 되나, List 처럼 임의 객체를 허용하는 매개변수화 타입은 괜찮다.

로 타입인 List와 매개변수화 타입인 List 의 차이는 List는 제네릭 타입에서 완전히 발을 뺀 것이고, List는 모든 타입을 허용한다는 의사를 컴파일러에 명확히 전달한 것이다.

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

이는 제네릭의 하위 타입 규칙 때문이다.

즉, List은 로 타입인 List의 하위 타입이지만, List의 하위 타입은 아니다.


추가 설명 요약

제네릭은 불변 타입으로 타입 파라미터의 상속 관계에 영향을 받지 않는다.

public class GenericExtendTest {

    public static void main(String[] args) {
        B b = new B();
        classExtendTest(b);
        List<B> bList = new ArrayList<>();
        genericExtendTest(bList); // 컴파일 에러
    }

    public static class A {}

    public static class B extends A {}

    public static void genericExtendTest(List<A> a) {}

    public static void classExtendTest(A a) {}
}

실제로 위의 코드에서 genericExtendTest 에서 컴파일 에러가 발생하는 것을 확인할 수 있다.

추가 설명 링크 : https://okky.kr/questions/354841


위의 예시를 보면 로 타입을 쓰고 싶다는 생각을 할 수 있다.

하지만 로 타입을 사용해서는 안전하지 않을 수 있기에 비한정적 와일드카드 타입을 대신 사용하는 것이 좋다.

제네릭 타입을 쓰고 싶지만 실제 타입 매개변수가 무엇인지 신경 쓰고 싶지 않다면 "?"를 사용하자.

와일드 카드 타입은 안전하고, 로 타입은 안전하지 않다.

컬렉션을 예로 들면 로 타입 컬렉션에는 아무 원소나 넣을 수 있으니 타입 불변식을 훼손하기 쉽다.

반면 와일드 카드 타입에는 null 외에는 어떤 원소도 넣을 수 없다.

다른 원소를 넣으려 하면 컴파일 오류를 발생시킨다.

즉, 컬렉션의 타입 불변식을 훼손하지 못하게 막았다.

하지만 이는 어떤 원소도 컬렉션에 넣지 못하게 했으며 컬렉션에서 꺼낼 수 있는 객체의 타입도 전혀 알 수 없게 했다.

이러한 제약을 받아들일 수 없다면 제네릭 메서드나 한정적 와일드카드 타입을 사용하면 된다.

하지만 로 타입을 쓰지 말라는 규칙에도 예외가 있다.

class 리터럴에는 로 타입을 사용해야 한다.

그리고 런타임에는 제네릭 타입 정보가 지워지므로 instanceof 연산자는 비한정적 와일드카드 타입 이외의 매개변수화 타입에는 적용할 수 없다.

그리고 로 타입이든 비한정적 와일드카드 타입이든 instanceof는 완전히 똑같이 동작한다.

비한정적 와일드카드 타입의 꺽쇠괄호와 물음표는 아무런 역할 없이 코드만 지저분하게 만들므로, 차라리 로 타입으로 사용하는 것이 깔끔하다.

0개의 댓글