Skip to content

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)

Section titled “재귀적 타입 한정(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:

Java