네로개발일기

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

# 아이템26. 로타입은 사용하지 마라

 

## 용어 정리
public class<T> Example {
  private T member;
}
- 제네릭 클래스[인터페이스]: 클래스[인터페이스] 선언에 타입 매개변수(type parameter)가 쓰인다.
- 제네릭 타입(Generic Type): 제네릭 클래스와 제네릭 인터페이스를 통틀어 말함. Example<T>
- 매개변수화 타입(parameterized Type): 각각의 제네릭 타입은 parameterized Type을 선언함. Example<String>
- 타입 매개변수(Type parameter): 제네릭 선언에 사용된 매개변수 <T>
- formalType: Example<E>
- actualType: Exampe<String>
- 로타입(raw tyep): 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않았을 때

 

## 제네릭의 타입 안정성
오류는 가능한 한 발생 즉시, 이상적으로는 컴파일 타임에 발견하는 것이 좋다.

 

### 1. 로타입의 단점: 컴파일 타임에 타입 정보를 알지 못한다.
ClassCastException과 같은 런타임에야 알아챌 수 있는 에러를 만들기 쉽다.
아래 예제는 컴파일 타임에는 문제가 없으나, 런타임에서 ClassCastException이 발샌한다.
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);
    }
}

 

### 2. 제네릭의 장점: 컴파일 타임에 타입 선언이 녹아있다.
컴파일러가 타입 선언에 대해 인지하고 있기 때문에, 아무런 경고 없이 컴파일되면 의도대로 동작할 것임을 보장한다.
public class Raw {
    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        unsafeAdd(strings, "42");   // 컴파일 타임에 에러를 체크해주어서 바꿀 수 있다.
        String s = strings.get(0);
    }

    private static <T> void unsafeAdd(List<T> list, T o) { // 제네릭 선언
        list.add(o);
    }
}
컴파일러가 컬렉션에서 원소를 넣는 모든 곳에 보이지 않은 형변환을 추가하여 절대 실패하지 않음을 보장하였다.

 

## 로타입은 절대로 사용하지 말자
제네릭이 안겨주는 안정성과 표현력을 모두 잃게 된다.
### 로타입이 만들어진 이유 - 호환성
기존 코드를 모두 수용하면서 제네릭을 사용하는 새로운 코드와 맞물려 돌아가게 하기 위해서이다.
호환성: 로타입 지원 + 제네릭 구현 시, 소거(erasure) 방식을 이용

 

** List VS List<Object>
- List: 로타입
- List<Object>: 임의 객체를 허용하는 매개변수화 타입 - 타입에 대한 정보를 컴파일러에 알려주었다.

 

로타입을 사용하면 타입 안정성을 잃게된다.

 

## 타입 안전 + 유연: 비한정 와일드카 타입
- 비한정 와일드카드 타입(unbounded wildcard type): 제네릭 타입을 쓰고 싶지만, 실제 타입 매개변수가 무엇인지 신경쓰고 싶지 않을 대 사용한다. Set<E>의 비한정 와일드카드 타입은 Set<?>
static int numElementsInCommon(Set<?> s1, Set<?> s2) {...}
와일드카드 타입은 안전하지만 로타입은 안전하지 않다.

 

- 로타입: 아무 원소나 넣을 수 있어 타입 불변식을 훼손하기 쉽다.
- 와일드카드 타입: Collection<?>에는 어떤 원소도 넣을 수 없다. (null 외에는) 컴파일 타임에 오류 메시지를 볼 수 있고, 컬렉션의 타입 불변식을 훼손하지 못하게 막고, 컬렉션에서 꺼낼 수 있는 객체의 타입도 알 수 없게 한다.
 
## 로타입 규칙 예외
### 1. class 리터럴에는 로타입을 사용한다.
자바 명세 자체가 class 리터럴에 매개변수화 타입을 사용하지 못하게 하였다.
- 허용: List.class, String[].class, int.class
- 불가: List<String>.class, List<?>.class

 

### 2. instanceof 연산자는 로타입을 사용한다.
instanceof 연산자는 비한정 와일드카드 타입 이외의 매개변수화 타입에는 적용할 수 없다.
로타입이든 비한정적 와일드타입이든 instanceof는 똑같이 동작한다.
if (o instanceof Set) { // 로타입
  Set<?> s = (Set<?>) o; // 와일드카드 타입
}
728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !