"이왕이면 제네릭 타입으로 만든 것 처럼 메서드도 이왕이면 제네릭 메서드로 만들자"
매개변수화 타입을 받는 정적 유틸리티 메서드는 보통 제네릭이다
매개변수화 타입? 제네릭 타입이 인스턴스화 되었을 때의 타입
ex) Collections.binarySearch 메서드
public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T key) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
return Collections.indexedBinarySearch(list, key);
else
return Collections.iteratorBinarySearch(list, key);
}
모든 원소가 T의 수퍼타입과 비교 가능한 Comparable을 상속하고 있는 list와 T타입 key 를 비교한다.
cf) <? extends T> vs <? super T>
https://stackoverflow.com/questions/4343202/difference-between-super-t-and-extends-t-in-java
List<? extends Number> numbers = new ArrayList<Integer>();
List<? extends Number> numbers = new ArrayList<Double>();
List<? super Integer> intLists = new ArrayList<Integer>();
List<? super Integer> intLists = new ArrayList<Number>();
List<? super Integer> intLists = new ArrayList<Object>();
제네릭을 만드는 방법
-
단순한 제네릭 메서드 - 모든 매개변수타입이 같아야한다:
public static <E> List<E> union(List<E> list1, List<E> list2) { List<E> allList = new ArrayList<>(); allList.addAll(list1); allList.addAll(list2); return allList; }
단점: 유연성이 떨어짐
@DisplayName("단순한 제네릭 메서드 사용법")
@Test
void generic(){
List<Integer> list1 = Arrays.asList(1,2,3);
List<Integer> list2 = Arrays.asList(4,5,6);
assertThat(GenericMethod.union(list1, list2)).isEqualTo(Arrays.asList(1,2,3,4,5,6));
List<String> list3 = Arrays.asList("str1", "str2");
// assertThat(GenericMethod.union(list1, list3)).isEqualTo(Arrays.asList(1,2,3,4,5,6)); compileError
}
2. 불변객체가 제네릭타입이면 여러 타입으로 활용할 수 있게된다.
: 요청 타입 변수에 맞게 객체의 타입을 바꿔주는 "제네릭싱글턴팩터리" 필요
제네릭 싱글턴 팩터리를 사용하는 예 1) 함수객체 Collections.revoerseOrder()
List<String> str = Arrays.asList("a", "b", "c");
str.sort(Collections.reverseOrder()); // 이처럼 함수 내부에 들어가는 객체를 함수객체라고 함
public static <T> Comparator<T> reverseOrder() {
// T 타입으로 캐스팅된 Singleton ReverseComparator 반환, 타입정보는 소거됨으로 그때그때 캐스팅만 되는 것
return (Comparator<T>) ReverseComparator.REVERSE_ORDER;
}
private static class ReverseComparator
implements Comparator<Comparable<Object>>, Serializable {
private static final long serialVersionUID = 7207038068494060240L;
static final ReverseComparator REVERSE_ORDER
= new ReverseComparator();
public int compare(Comparable<Object> c1, Comparable<Object> c2) {
return c2.compareTo(c1);
}
private Object readResolve() { return Collections.reverseOrder(); }
@Override
public Comparator<Comparable<Object>> reversed() {
return Comparator.naturalOrder();
}
}
public static final <T> Set<T> emptySet() {
return (Set<T>) EMPTY_SET; // T 타입으로 변환해서 반환해주는 제네릭싱글턴팩터리
}
public static final Set EMPTY_SET = new EmptySet<>(); // 싱글턴객체
제네릭 싱글턴 팩터리를 사용하는 예 2) 재귀적 타입 한정
public static <E extends Comparable<E>> E max(Collection<E> c);
'모든타입 E는 자기자신과 같은 타입인 원소 모두와 비교가능하다' 라는 추측이 가능해짐
cf) 시뮬레이트한 셀프타입 관용구 - [아이템 2]
- 관련 자바봄 이슈) https://github.com/Java-Bom/ReadingRecord/issues/75
재귀적 타입 한정을 이용하는 제네릭타입.
추상메서드 self 를 지원하여 하위클래스에서 형변환 하지 않고도 메서드 연쇄를 지원할 수 있음.
self 타입이 없는 자바를 위한 우회방법
상위 클래스에서 재귀적 타입한정 클래스 Builder를 추상클래스로 제공
public abstract class PayCard {
public enum Benefit {
POINT("포인트"), SALE("할인"), SUPPORT("연회비지원");
Benefit(String benefit) {
}
}
final Set<Benefit> benefits;
abstract static class Builder<T extends Builder<T>> { // 재귀적 타입한정
EnumSet<Benefit> benefits = EnumSet.noneOf(Benefit.class);
public T addBenefit(Benefit benefit) {
this.benefits.add(benefit);
return self();
}
abstract PayCard build();
protected abstract T self();
}
PayCard(Builder<?> builder) {
benefits = builder.benefits.clone();
}
}
하위클래스에서는 아래와 같이 재구현. 타입캐스팅 없음
public static class Builder extends PayCard.Builder<Builder>{
private final Sale sale;
public Builder(Sale sale) {
this.sale = sale;
}
@Override
LottePayCard build() {
return new LottePayCard(this);
}
@Override
protected Builder self() {
return this;
}
}
아래와 같이 상위 클래스의 메서드 호출 가능
@DisplayName("시뮬레이트한 셀프타입 관용구")
@Test
void simulate(){
LottePayCard lottePayCard = new LottePayCard.Builder(LOTTE_DEPT)
.addBenefit(PayCard.Benefit.POINT)
.build();
}
'Reading Record > 이펙티브자바' 카테고리의 다른 글
이펙트브자바 제네릭 (item 26,27,28) (0) | 2020.04.07 |
---|---|
[아이템 31] 한정적 와일드카드를 사용해 API 유연성을 높여라 (0) | 2020.03.29 |
[아이템 29] 이왕이면 제네릭 타입을 사용하라 (0) | 2020.03.28 |
아이템 [6] - 불필요한 객체생성을 피하라 (0) | 2020.03.04 |
아이템 [20] - 추상 클래스 보다는 인터페이스를 우선하라 (1) | 2020.02.29 |