요약
이왕이면 형변환하는 기존 메서드는 제네릭 메서드로 바꾸자. 안정성과 사용성 면에서 더 낫다.
클라이언트에서 형변환하는 메서드는 되도록이면 만들지 말자.
제네릭 메서드란?
타입 매개변수를 사용하는 메서드를 의미한다.
제네릭 메서드 작성 방법
메서드 선언에서의 원소 타입을 타입 매개변수(E)로 명시하고, 메서드 안에서도 이 타입 매개변수만 사용하게 하면 된다.
// [접근제어자] static [타입 매개변수 목록] [반환타입] [함수명](파라미터)
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>
- 파라미터 2개, 반환 객체 1개 모두 같은 타입이어야 한다.
제네릭 싱글턴 팩터리
요청한 타입 매개변수에 맞게 매번 그 객체의 타입을 바꿔주는 정적 팩터리를 의미한다.
(기존 타입) → (요청한 타입 매개변수)
- 예시 : Collections.reverseOrder, Collections.emptySet
@SuppressWarnings("unchecked")
public static <T> Comparator<T> reverseOrder() {
return (Comparator<T>) ReverseComparator.REVERSE_ORDER;
}
@SuppressWarnings("unchecked")
public static final <T> Set<T> emptySet() {
return (Set<T>) EMPTY_SET;
}
예시 : 항등함수
항등함수는 입력 값을 수정 없이 그대로 반환하는 특별한 함수를 의미한다.
private static UnaryOperator<Object> IDENTITY_FN = (t) -> t;
@SuppressWarnings("unchecked")
public static <T> UnaryOperator<T> identityFunction() {
return (UnaryOperator<T>) IDENTITY_FN;
}
- 필드 IDENTITY_FN의 타입 = UnaryOperator<Object>
- identityFunction()의 반환 타입 = UnaryOperator<T>이다.
- 따라서 UnaryOperator<Object> ≠ UnaryOperator<T> 이기에 비검사 형변환 경고가 발생
- 이때 항등함수는 입력 값을 수정 없이 그대로 반환하는 함수이므로, 타입 안전하다.
- 위에서 발생한 비검사 형변환 경고는 숨겨도 안전하기에 @SuppressWarning(”unchecked”) 설정
재귀적 타입 한정(recursive type bound)
자기 자신이 들어간 표현식(<E extends Comparable<E>>)을 활용해 타입 매개변수의 허용 범위를 한정하는 개념이다. 재귀적 타입 한정은 주로 타입의 자연적 순서를 정하는 Comparable 인터페이스와 함께 쓰인다.
public interface Comparable<T> {
int compareTo(T o);
}
new Comparable<String> -> String 타입만 사용가능
- compareTo()의 매개변수 T는 Comparable<T>를 구현한 타입이 비교할 수 있는 원소 타입을 정의한다 → 한 타입만 사용하도록 강제시키기 때문에 타입 안정성 보장
- String은 Comparable<String>을 구현, Integer는 Comparable<Integer>를 구현하는 식
// 파라미터 타입 Collection<E>, 타입 파라미터 목록이 <E extends Comparable<E>>이므로
// 파라미터에 E를 포함한 자식 타입만 입력될 수 있다는 의미?
// 컬렉션에서 최댓값 반환
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;
}
- 파라미터 타입이 Comparable 컬렉션인 경우, 컬렉션 원소의 정렬/검색/최솟값/최댓값을 구하는 것
- Comparable<T>를 상속한 타입만 들어가게 보장할 수 있음 → 타입 안정성 보장
- 컬렉션에 담긴 모든 원소가 상호 비교될 수 있어야함 → 컬렉션에 담긴 모든 원소가 같은 타입이어야 한다.
'Dev Language > EffectiveJava' 카테고리의 다른 글
[EffectiveJava]비트 필드 대신 EnumSet을 사용하라 (2) | 2025.01.25 |
---|---|
[EffectiveJava] int 상수 대신 열거 타입(Enum)을 사용하라 (2) | 2025.01.18 |
[EffectiveJava]이왕이면 제네릭 타입으로 만들라 (0) | 2025.01.05 |
[EffectiveJava] 로 타입(raw type)은 사용하지 말라 (4) | 2024.12.29 |
[EffectiveJava] 멤버 클래스는 되도록 static으로 만들라 (0) | 2024.12.22 |