Search
🙊

이펙티브 자바:: 아이템 30 <이왕이면 제네릭 메서드로 만들라>

Intro::

이펙티브 자바 정리본입니다.

결론

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

제네릭 메서드

(타입 매개변수들을 선언하는) 타입 매개변수 목록은 메서드의 제한자반환 타입 사이에 온다.
public class Union { // 코드 30-2 제네릭 메서드 (177쪽) public static <E> Set<E> union(Set<E> s1, Set<E> s2) { Set<E> result = new HashSet<>(s1); result.addAll(s2); return result; } }
Java
복사

불변 객체를 여러 타입으로

요청한 타입 매개변수에 맞게 매번 그 객체의 타입을 바꿔주는 정적 팩터리를 만들어야 합니다. 이 패턴을 제네릭 싱글턴 팩터리라 하며, Collections.reverseOrder 같은 함수객체나 Collections.emptySet과 같은 컬렉션용으로 사용합니다.
public class ReverseOrderComparator<T> implements Comparator<T> { private static final Comparator<?> INSTANCE = new ReverseOrderComparator<>(); private ReverseOrderComparator() {} // private constructor to enforce singleton @Override public int compare(T o1, T o2) { return ((Comparable<T>) o2).compareTo(o1); } // 제네릭 싱글턴 패턴을 적용한 정적 팩터리 메서드 @SuppressWarnings("unchecked") public static <T> Comparator<T> reverseOrder() { return (Comparator<T>) ReverseOrderComparator.INSTANCE; } }
Java
복사

항등함수를 담은 클래스

항등함수 객체는 상태가 없으니 요청할 때마다 새로 생성하는 것은 낭비입니다.
private static UnaryOperator<Object> IDENTITY_FN = (t) -> t; @SuppressWarnings("unchecked") public static <T> UnaryOperator<T> identityFunction() { return (UnaryOperator<T>) IDENTITY_FN; }
Java
복사
항등함수란 입력 값을 수정 없이 그대로 반환하는 특별한 함수이므로, T가 어떤 타입이든 UnaryOperator<T>를 사용해도 타입 안전합니다.

재귀적 타입 한정

public class RecursiveTypeBound { // 코드 30-7 컬렉션에서 최댓값을 반환한다. - 재귀적 타입 한정 사용 (179쪽) public static <E extends Comparable<E>> E max(Collection<E> c) { if (c.isEmpty()) throw new IllegalArgumentException("컬렉션이 비어 있습니다."); E result = null; for (E e : c) if (result == null || e.compareTo(result) > 0) result = Objects.requireNonNull(e); return result; } }
Java
복사
<E extends Comparable<E>>는 간단하게 말해서 “자기 자신끼리 비교 가능하다면 ~” 이라는 의미이다.
어떠한 타입이든 해당타입의 Comparable을 구현한 타입이라는 의미에서 재귀적이라는 표현이 붙는다.

질문

제네릭 싱글턴 팩터리가 정확히 뭘까??

// Collections 제네릭 싱글턴 팩터리 메서드 예시 @SuppressWarnings("rawtypes") public static final Set EMPTY_SET = new EmptySet<>();// (1) EMPTY_SET은 immutable하다 public static final <T> Set<T> emptySet() {// (2) return (Set<T>) EMPTY_SET;// (3) }
Java
복사
위의 예시에서 볼 수 있듯이 (1)에서 로타입으로 가지고 있는 비어있는 Set 정적값을 (2)의 제네릭 타입 매개변수로 타입을 추론하여 형변환을 해 반환해 줍니다. 즉, 제네릭 싱글턴 팩터리란 요청한 매개변수에 맞게 해당 싱글턴 객체의 타입을 바꾸어주는 방식이라고 이해하면 됩니다.

항등함수 객체는 상태가 없으니 요청할 때 마다 새로 생성하는 것은 낭비라는것에 대한 예시가 이해가 안간다.

private static UnaryOperator<Object> IDENTITY_FN = (t) -> t; @SuppressWarnings("unchecked") public static <T> UnaryOperator<T> identityFunction() { return (UnaryOperator<T>) IDENTITY_FN; }
Java
복사
UnaryOperator 는 단일 인자를 받아서 같은 타입의 값을 반환하는 함수형 인터페이스입니다.
public static <T> UnaryOperator<T> identityFunction() { return t -> t; }
Java
복사
물론 위와 같이 항등함수를 생성해서 반환해도 됩니다. 하지만 성능상 이점을 위해 이전 코드와 같이 작성이 가능합니다.
여기서 IDENTITY_FN는 항등 함수 객체를 재사용하기 위해 메모리에 Object 타입으로 미리 만들어 놓은 상태이며, identityFunction에서 형변환을 통해 재사용을 하는 것입니다.

References::

이펙티브 자바 / 조슈아 블로크 지음 (프로그래밍 인사이트)