Item 30. Generic Method

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

메서드도 제네릭으로 만들 수 있는데, 이를 제네릭 메서드라고 하며, Collectionssort 메서드가 대표적인 예이다. 명명 규칙이나 사용 방법은 제네릭 클래스와 동일하다.


class Test {

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

    public static void main(String[] args) {
        Set<String> a = Set.of("a", "b", "c");
        Set<String> b = Set.of("d", "e", "f");
        Set<String> c = union(a, b);
    }
}

마찬가지로 한정적 와일드카드 타입으로 더 유연하게 만들 수 있다.

제네릭 싱글턴 팩터리

불변 객체를 여러 타입으로 활용할 수 있게 만들어야 할 때가 있다. 제네렉은 런타임에 타입 정보가 소거되기 때문에 하나의 객체를 어떤 타입으로 매개변수화 할 수 있는데, 이를 위해선 타입을 바꿔주는 정적 팩터리를 만들어야 한다. 정적 팩터리 메서드에서는 요청한 타입 매개변수에 맞게 그 객체의 타입을 바꿔주는 역할을 수행하게 되며, 이 패턴을 제네릭 싱글턴 팩터리라고 한다.

// Collections에서 사용된 정적 팩터리 메서드 reverseOrder
public class Collections {
    // Suppresses default constructor, ensuring non-instantiability.
    private Collections() {
    }

    // ...

    @SuppressWarnings("unchecked")
    public static <T> Comparator<T> reverseOrder() {
        return (Comparator<T>) ReverseComparator.REVERSE_ORDER;
    }

    // ...
}

제네릭 싱글턴 팩터리를 항등함수를 담은 클래스의 메서드에 적용하면 아래와 같이 사용할 수 있다.

class GenericSingletonFactory {
    private static final UnaryOperator<Object> IDENTITY_FN = (t) -> t;

    @SuppressWarnings("unchecked") // 수정 없이 그대로 반환하므로 안전(=타입 안정성 보장)
    public static <T> UnaryOperator<T> identityFunction() {
        return (UnaryOperator<T>) IDENTITY_FN; // UnaryOperator<Object> != UnaryOperator<T> 으로 경고 발생
    }
}

class Main {
    public static void main(String[] args) {
        String[] strings = {"오리", "너구리", "오구"};
        // String을 인수로 받아 String을 반환하는 UnaryOperator
        UnaryOperator<String> sameString = GenericSingletonFactory.identityFunction();
        for (String s : strings) System.out.println(sameString.apply(s));

        Number[] numbers = {5, 9.0, 59L};
        // Number를 인수로 받아 Number를 반환하는 UnaryOperator
        UnaryOperator<Number> sameNumber = GenericSingletonFactory.identityFunction();
        for (Number n : numbers) System.out.println(sameNumber.apply(n));
    }
}

재귀적 타입 한정(recursive type bound)

자기 자신이 들어간 표현식을 사용해 타입 매개변수의 허용 범위를 한정하는 재귀적 타입 한정이라는 개념도 존재한다. 재귀적 타입 한정은 주로 Comparable 인터페이스와 함께 사용한다.

// Comparable 인터페이스
public interface Comparable<T> {
    int compareTo(T o);
}

// java.util.Collections의 max 메서드
public class Collections {
    // Suppresses default constructor, ensuring non-instantiability.
    private Collections() {
    }

    // ...

    public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) {
        Iterator<? extends T> i = coll.iterator();
        T candidate = i.next();

        while (i.hasNext()) {
            T next = i.next();
            if (next.compareTo(candidate) > 0)
                candidate = next;
        }
        return candidate;
    }

    // ...
}

값을 상호 비교하기 위해서는 컬렉션에 담긴 모든 원소가 상호 비교될 수 있어야 한다. Collectionmax 메서드도 정상적으로 기능을 수행하기 위해선 상호 비교가 되야하므로 TComparable을 구현하도록 제한하고 있다.

Last updated

Was this helpful?