Effective Java 2판 - 5장 정리 (Generic)

용어
형인자 자료형(parameterized type) List<String>
실 형인자(actual type parameter) String
제네릭 자료형(generic type) List<E>
형식 형인자(formal type parameter) E
비한정적 와일드카드 자료형(unbounded wildcard type) List<?>
무인자 자료형(raw type) List
한정적 형인자(bounded type parameter) <E extends number>
재귀적 형 한정(recursive type bound) <T extends Comparable<T>>
한정적 와일드카드 자료형(bounded wildcard type) List<? Extends Number>
제네릭 메서드(generic method) Static <E> List<E> asList(E[] a)
자료형 토큰(type token) String.class

규칙23: 새 코드에는 무인자 제네릭 자료형을 사용하지 마라.

무인자 자료형: 예를 들어 List<E>의 무인자 자료형은 List이다.

무인자 자료형을 쓰면 형 안전성이 사라지고 제네릭의 장점 중 하나인 표현력 측면에서 손해를 보게 된다.

무인자 자료형을 사용하면 런타임 도중 예외가 발생할 수 있으므로 새로 만드는 코드에는 무인자 자료형을 사용하지 말아야 한다.

 

규칙24: Unchecked warning을 제거하라.

Generic 사용시 많은 unchecked warning을 마주하기 쉽다.

가능한 한 모두 제거하되 제거할 수 없는 메시지는 형 안전성이 확실할 때만 @SuppressWarnings annotation을 사용해라. 또한 가능한 한 작은 범위에 적용해라. 그리고 사용할 때마다 왜 형 안전성을 위반하지 않는지 밝힌느 주석을 반드시 밝혀라.

 

규칙25: 배열 대신 리스트를 써라.

배열은 공변 자료형(covariant)이다. 이는 subsuper의 하위 자료형이라면 sub[]super[]의 하위 자료형이라는 것이다. 반면 제네릭은 불변 자료형(invariant)이다. Type1Type2가 있을 때 List<Type1>List<Type2>의 상위 자료형이나 하위 자료형이 될 수 없다.

배열의 각 원소 자료형은 runtime에 결정된다. 반면 제네릭은 compile 시점에만 적용되고 그 정보는 프로그램 실행 시 삭제된다.

서로 규칙이 다르기 때문에 쉽게 혼용할 수 없다.

 

규칙26: 가능하면 제네릭 자료형으로 만들 것

제네릭 자료형은 클라이언트가 형변환을 해야만 사용할 수 있는 자료형보다 안전할 뿐만 아니라 사용하기도 쉽다. 기존 클라이언트 코드를 깨지 않고도 새로운 사용자에게 더 좋은 API를 제공할 수 있게 될 것이다.

 

규칙27: 가능하면 제네릭 메서드로 만들 것

클래스뿐만 아니라 메서드도 제네릭의 혜택을 볼 수 있다. 여러 binarySearch, sort와 같은 것은 제네릭으로 구현되어 있다.

형인자를 선언하는 형인자 목록은 메서드 수정자의 반환값 자료형 사이에 둔다.

// generic static factory method

public static <K, V> HashMap<K, V> newHashMap(){

           return new HashMap<K, V>();

}

// generic singleton factory pattern
Private static UnaryFunction<Object> IDENTITY_FUNCTION =

           New UnaryFunction<Object>(){

                      Public Object apply(Object arg) {return arg;}

           };

 

제네릭 자료형처럼 제네릭 메소드는 클라이언트가 직접 입력 값과 반환값의 자료형을 형변환해야하는 메서드보다 사용하기 쉽고 형 안전성도 높다.

 

규칙28: 한정적 와일드카드를 써서 API 유연성을 높여라.

유연성을 최대화하려면 객체의 producerconsumer 구실을 하는 메소드 인자의 자료형은 와일드카드 자료형으로 하는 것이 좋다. 어떤 와일드카드를 쓸지 어렵다면 아래 약어를 활용하자.

PECS -> (Produce – Extends, Consumer – Super)

 

다시 말해서 인자가 T 생산자라면 <? Extends T>라고 하고 T 소비자라면 <? Super T>라고 해라.

단 반환값에는 와일드카드 자료형을 사용해서는 안 된다. 클래스 사용자가 와일드카드 자료형에 대해 고민하게 된다면 그것은 아마도 클래스 API가 잘못 설계된 탓일 것이다.

 

규칙29: 형 안전 다형성 컨테이너를 쓰면 어떨지 따져보라.

보통 container 별로 형인자 수는 고정된다. 예를 들어 Set은 하나, Mapkeyvalue 하나씩 필요할 것이다. 하지만 좀 더 유연한 방법이 필요할 수 있다. 예를 들어 DB에 저장되는 레코드의 경우 임의의 개수의 column을 가진다. 이 때 typesafe을 깨지 않으면서 각 열에 접근할 방법이 있다면 좋을 것이다.

-> Container 대신 key에 형인자를 지정하고 container에 값을 넣거나 뺄 때마다 key를 제공한다.