이펙티브 자바 다시 읽기 5장 제네릭(아이템26~33)

Kohalahal·2020년 7월 16일
0

자바국비

목록 보기
8/8

제네릭은 자바 5부터 나왔는데 이전에는 컬렉션에서 객체 꺼낼 때 마다 형변환을 해야했다. 그래서 런타임 형변환 오류 나곤 했다. 반면 제네릭은 컬렉션이 담을 수 있는 타입을 컴파일러에 알려주게됨. 그래서 컴파일러가 알아서 형변환 코드 추가, 엉뚱타입 객체 넣으려는 시도를 컴파일 과정에서 차단.

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

List<E>

같이 클래스와 인터페이스 선언에 타입 매개변수가 쓰이면 제네릭 클래스/제네릭 인터페이스임.

그런 걸 로 타입(실제 매개변수 타입 지정 안하고)으로 쓰지 말라. 제네릭의 이점인 안정성, 표현력 모두 잃는다.

컴파일에서 오류 발견 못하고 한참 뒤 런타임에 알아채게 된다.

타입 지정해서 타입 안정성 확보하라. 만약에 Object로 지정한다면, 그것은 컴파일러에게 모든 타입을 허용한다는 것을 정확히 전달한 것이라서 로 타입과 다르다.

원소 타입 몰라도 되는 걸 작성하고 싶으면 비한정적 와일드카드 타입unbounded wildcard type을 써라.


List<?>

로타입과 달리 안전하다. 로 타입에는 아무 원소나 넣을 수 있지만 ?에는 null외에 어떤 타입도 넣을 수 없다.

소소한 예외
: class 리터럴에는 로 타입 , instanceof연산자에는 로 타입

)List.class -> OK

List<String>.class -> NG

if(o instanceof Set) { -> OK

    Set<?> s = (Set<?>) o;
}

아이템27 비검사 경고 제거하라

제네릭 쓰면 컴파일러 경고 많이 뜰 것이다. 타입 안전 확신하면 @SuppressWarnings("unchecked") 달라.

근데 왜 안전한지 이유를 항상 주석으로 남겨라.

아이템28 배열보다 리스트를 사용하라

배열과 제네릭의 중요한 차이 두 가지

  1. 배열은 공변
    : 제네릭은 불공변
  2. 배열은 실체화(런타임에도 자신의 원소 타입 인지하고 확인)
    : 제네릭은 타입 정보가 런타임에는 소거(실체화될 수 있는 타입은 비한정적 와일드카드 타입 뿐)

그래서 배열 쓰면 런타임 오류가 날 걸 리스트 쓰면 컴파일때 알 수 있다.

제네릭 배열은 생성 안되게 막혀있다. 타입 안전하지 않기 때문. (비한정적 와일드 카드 타입은 만들 수 있다)

아이템29 이왕이면 제네릭 타입

클래스를 제네릭으로 만들 때 만약에 배열 사용하려면 발목을 잡네.

두 가지 해결책(예시는 배열로 stack 클래스 만든 것임)

  1. 대놓고 제약 우회하기
    : Object 배열 생성한 다음에 제네릭 배열로 형변환하기
    : 오류가 경고가 되지만 타입 안전하지 않다.
    : 형변환은 처음 생성할 때 한번만 해주면 됨.
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
  1. elements를 E[]에서 Object[]로 아예 선언한다.
    : 그리고 배열이 반환한 원소를 E로 형변환 한다.
    : 그러면 경고뜨는데 숨길 수 있겠지.
    : 배열에서 원소 읽을때 마다 형변환 해줘야.
위에
Object[] elements = new Object[DEFAULT_INITIAL_CAPACITY];
...
public E pop() {
    if (size == 0) new EmptyStackException();

    @SupressWarnings("unchecked") E result = (E) elements[--size];
    elements[size] = null;
    return result;
}

아이템30 이왕이면 제네릭 메서드

클래스처럼 메서드도 제네릭으로 만들 수 있다.

예)


public static Set union(Set s1, Set s2) {
    Set result = new HashSet(s1);
    result.addAll(s2);
    return result;
} -> 로 타입 사용 NG

public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
    Set<E> result = new HashSet<>(s1);
    result.addAll(s2);
    return result;
} -> 굿 굿

아이템31 한정적 와일드카드 사용해 API 유연성 높여라


Collection<? super E> collection 

유연성을 극대화하려면 원소의 생산자나 소비자용 입력 매개 변수에 와일드 카드 타입을 사용하라.

반환 타입에는 한정적 와일드 카드 타입을 사용하면 안 된다. 유연성 높이는게 아니라 클라이언트 코드에서도 와일드 카드 타입을 써야하기 때문.

PECS 공식-어떤 와일드 카드 타입을 써야 하는지

producer-extends, consumer-super

아이템32 제네릭과 가변인수 함께 쓸 때는 신중하라

varargs 매개변수에 제네릭이나 매개변수화 타입이 포함되면 힙 폴루션 경고 난다.

아이템33 타입 안전 이종 컨테이너를 고려하라

0개의 댓글