표준 함수형 인터페이스를 사용하라
템플릿 메서드 패턴(상위 클래스의 기본 메서드 재정의)은 같은 효과의 함수 객체를 받는 정적 팩터리나 생성자를 제공하는 방법으로 대체될 수 있다. 이때 함수형 매개변수 타입을 올바르게 선택해야 한다.
LinkedHashMap 클래스의 protected 메서드인 removeEldestEntry
를 재정의해 캐시로 사용할 수 있다.
Map에 새로운 키를 추가하는 put 메서드는 해당 메서드를 호출해 true가 반환되면 맵에서 가장 오래된 원소를 제거한다.
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > 100;
}
위와 같이 재정의한다면, 100개까지 재한하고 그 이후부터 원소를 제거하여 최근 원소 100개는 유지하도록 한다.
위에서 size()는 removeEldestEntry
가 Map
의 메서드라 호출이 가능하지만, 생성자에 넘기는 함수 객체는 이 맵의 인스턴스 메서드가 아니어서 자기 자신도 함수 객체에 내어줘야 한다.
@FuntionalInterface interface EldestEntryRemovalFunction<K, V> {
boolean remove(Map<K, V> map, Map.Entry<K,V> eldest);
}
하지만 이 인터페이스는 자바 표준 라이브러리(java.util.function)에 존재하므로 직접 구현할 필요가 없다. 필요한 용도에 맞는 게 있다면, 직접 구현하지말고 표준 함수형 인터페이스를 활용해야한다.
제목 | 내용 | 설명 | 설명 |
---|---|---|---|
UnaryOperator | T apply(T t) | 반환값과 인수의 타입이 같은 함수, 인수는 1개 | String::toLowerCase |
BinaryOperator | T apply(T t1, T t2) | 반환값과 인수의 타입이 같은 함수, 인수는 2개 | BigInteger::add |
Predicate | boolean test(T t) | 한 개의 인수를 받아서 boolean을 반환하는 함수 | Collection::isEmpty |
Function<T,R> | R apply(T t) | 인수와 반환 타입이 다른 함수 | Arrays::asList |
Supplier | T get() | 인수를 받지 않고 값을 반환, 제공하는 함수 | Instant::now |
Consumer | void accept(T t) | 한 개의 인수를 받고 반환값이 없는 함수 | System.out::println |
표준 함수형 인터페이스 대부분은 기본 타입만 지원하지만 박싱된 기본 타입을 넣어 사용하지 않는 것이 좋다.
이 애너테이션의 의미는 다음과 같다.
따라서 함수형 인터페이스는 항상 해당 애너테이션을 달 필요가 있다.
서로 다른 함수형 인터페이스를 같은 위치의 인수로 받는 메서드들을 다중 정의해서는 안된다. (아이템 52)
public interface ExecutorService extends Executor {
// Callable<T>와 Runnable을 각각 인수로 하여 다중정의했다.
// submit 메서드를 사용할 때마다 형변환이 필요해진다.
<T> Future<T> submit(Callback<T> task);
Future<?> submit(Runnable task);
}
위의 코드는 올바른 메서드를 알려주기 위해 형변환해야 할 때가 왕왕 생기게 된다.
https://madplay.github.io/post/favor-the-use-of-standard-functional-interfaces