본문 바로가기

책/Effective Java

제네릭

로 타입은 사용하지 말라

 - 안 좋은 예

final Collection stamps = ...;

for (Iterator i = stamps.iterator(); i.hasNext();) {

Stamp stamp = (Stamp) i.next();

...;

}

 - 좋은 예

final Collection<Stamp> stamps = ...;

for (Stamp i : stamps) {

Stamp stamp = i;

...;

}


비검사 경고를 제거하라

 - 할 수 있는 한 모든 비검사 경고를 제거하라.

 - 경고를 제거할 수는 없지만 타입 안전하다고 확신할 수 있다면 @SuppressWarnings("unchecked") 애너테이션을 달아 경고를 숨기자. (가능한 좁은 범위에 사용하고, 경고를 무시해도 되는 안전한 이유를 항상 주석으로 남겨야 한다.)


배열보다는 리스트를 사용하라

 - 배열은 공변이고 실체화 되는 반면, 제네릭은 불공변이고 타입 정보가 소거 된다.

   * 공변 : Sub가 Super의 하위 타입이라면 배열 Sub[]는 배열 Super[]의 하위 타입이 된다.

 - 배열은 런타임에는 타입 안전하지만 컴파일타임에는 그렇지 않다. 제네릭은 반대다.

 - 둘을 섞어 쓰다가 컴파일 오류나 경고를 만나면, 가장 먼저 배열을 리스트로 대체하는 방법을 적용해보자.


이왕이면 제네릭 타입으로 만들라

 - 클라이언트에서 직접 형변한해야 하는 타입보다 제네릭 타입이 더 안전하고 쓰기 편하다.

 - ex) 

public class Stack {

private Object[] elements;

...

} (X)

public class Stack<E> {

private E[] elements;

...

} (O)


이왕이면 제네릭 메서드로 만들라

 - 제네릭 타입과 마찬가지로, 클라이언트에서 입력 매개변수와 반환값을 명시적으로 형변환해야 하는 메서드보다 제네릭 메서드가 더 안전하고 사용하기 쉽다.

 - 안 좋은 예

public static Set union(Set s1, Set s2) {

Set result = new HashSet(s1);

result.addAll(s2);

return result;

 - 좋은 예

public static <E> Set<E> union(Set<E> s1, Set<E> s2) {

Set<E> result = new HashSet<>(s1);

result.addAll(s2);

return result;

}


한정적 와일드카드를 사용해 API 유연성을 높이라

 - public void pushAll(Iterable<E> src) (X) : public void pushAll(Iterable<? extends E> src) (O)

 - public void popAll(Collection<E> dst) (X) : public void popAll(Collection<? super E> dst) (O)

 - 유연성을 극대화하려면 원소의 생산자나 소비자용 입력 매개변수에 와일드카드 타입을 사용하라. (생산자는 extends를 소비자는 super를 사용한다.)

 - 반환 타입에는 한정적 와일드카드 타입을 사용하면 안 된다.


제네릭과 가변인수를 함께 쓸 때는 신중하라

 - 제네릭 varargs 배열 매개변수에 값을 저장하는 것은 안전하지 않다.

 - 제네릭 varargs 매개변수 배열에 다른 메서드가 접근하도록 허용하면 안전하지 않다.

 - 제네릭이나 매개변수화 타입의 varargs 매개변수를 받는 모든 안전한 메서드에 @SafeVarargs 를 달자. (@SafeVarages 애너테이션은 메서드가 타입 안전함을 보장한다는 뜻이다.)


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

 - 컬렉션 API로 대표되는 일반적인 제네릭 형태에서는 한 컨테이너가 다룰 수 있는 타입 매개변수의 수가 고정되어 있다. 

 - 컨테이너 자체가 아닌 키를 타입 매개변수로 바꾸면 이런 제약이 없는 타입 안전 이종 컨테이너를 만들 수 있다.

 - 타입 안전 이종 컨테이너는 Class를 키로 쓰며, 이런 식으로 쓰이는 Class 객체를 타입 토큰이라 한다. 

Map<Class<?>, Object> favorites = new HashMap<>();

public <T> void put(Class<T> type, T instance) {

favorites.put(Objects.requireNonNull(type), instance);

}

public <T> T get(Class<T> type) {

return type.cast(favorites.get(type);

}

 - 직접 구현한 키 타입도 쓸 수 있다. ex) 데이터베이스의 행(컨테이너)을 표현한 DatabaseRow 타입에는 제네릭 타입인 Column<T>를 키로 사용할 수 있다.



' > Effective Java' 카테고리의 다른 글

람다와 스트림  (0) 2020.03.20
열거 타입과 애너테이션  (0) 2020.03.18
클래스와 인터페이스  (0) 2020.03.14
모든 객체의 공통 메서드  (0) 2020.03.13
객체 생성과 파괴  (0) 2020.03.10