클래스와 마찬가지로, 메서드도 제네릭으로 만들 수 있다.
ex. Collections의 알고리즘 메서드 (binarySearch, sort 등)
제네릭 메서드 작성법은 제네릭 타입 작성법과 비슷하다.
public class BadCase {
public static Set union(Set s1, Set s2) {
Set result = new HashSet<>();
result.addAll(s2);
return result;
}
}
- 컴파일은 되지만, 경고가 발생한다. (타입 안전하기 않기 때문에)
public class GoodCase {
public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
Set<E> result = new HashSet<>(s1);
result.addAll(s2);
return result;
}
}
- 타입안전하게 만들 수 있다.
- 타입 매개변수 목록은 메서드의 제한자와 반환타입 사이에 온다.
- 타입 매개변수 목록 :
<E>
- 반환 타입 :
Set<E>
- 타입 매개변수 목록 :
- 타입 매개변수의 명명규칙은 제네릭 메서드, 제네릭 타입이 모두 같다.
- 한정적 와일드카드 타입 (아이템 31)을 사용하여 더 유연하게 개선할 수 있다.
제네릭은 런타임에 타입 정보가 소거되므로, 하나의 객체를 어떤 타입으로든 매개변수화 할 수 있다.
이렇게 하기 위해서 요청한 타입 매개 변수에 따라 불변 객체의 타입을 요청한 타임 매개변수에 맞게 변경해주는 정적 팩토리를 만들어야 한다.
public class GenericSingleton {
private static UnaryOperator<Object> IDENTITY_FN = (t) -> t;
@SuppressWarnings("unchecked")
public static <T> UnaryOperator<T> identityFunction() {
return (UnaryOperator<T>) IDENTITY_FN;
}
}
- 해당 예시는 항등 함수를 사용하고 있으며, 어떤 타입이어도 타입 안전하기 떄문에
SuppressWarnings("unchecked")
를 사용해서 오류를 제거한다.
상대적으로 드물지만, 자기 자신이 들어간 표현식을 사용하여 타입 매개변수의 허용 범위를 한정할 수 있다.
이는 주로 Comparable 인터페이스 (아이템 14)와 함께 쓰인다.
public interface Comparable<T> {
int compareTo(T o);
}
여기서 타입 매개변수 T는 Comparable을 구현한 타입이 비교할 수 있는 원소의 타입을 정의하며, 거의 모든 타입은 자신과 같은 타입의 원소와만 비교할 수 있다.
public class RecursiveTypeBoundEx {
public static <E extends Comparable<E>> E max(Collection<E> c);
}
타입 한정인 <E extends Comparable<E>>
는 "모든 타입 E는 자신과 비교할 수 있다"라고 읽을 수 있다. 즉, 상호 비교 가능하다는 뜻이다.
public class RecursiveTypeBoundEx {
public static <E extends Comparable<E>> E max(Collection<E> collection) {
if (collection.isEmpty()) {
throw new IllegalArgumentException("비어 있습니다.");
}
E result = null;
for (E e : collection) {
if (result == null || e.compareTo(result) > 0) {
result = Objects.requireNonNull(e);
}
}
return result;
}
}
- 이번 아이템에서 설명한 내용들은 와일드카드를 사용한 변형 (아이템 31), 셀프 타입 관용구(아이템 2)를 이해하면 더 잘 활용할 수 있을 것이다.
- 명시적으로 형변환하는 메서드보다 제네렉 메서드가 더 안전하고, 사용하기 쉽다.
- 형변환을 해줘야하는 기존 메서드는 제네릭하게 만들자. (아이템 26 연관됨)