이펙티브 자바, 쉽게 정리하기 - item 44. 표준 함수형 인터페이스를 사용하라
람다를 활용할 때 표준 함수형 인터페이스를 사용하자
- 자바에서는 람다에 활용하라고 미리 만들어둔 표준 함수형 인터페이스들이 존재한다.
removeEldestEntry()의 예
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > 100;
}
LinkedHashMap내부 메서드이다.- 맵의 키 밸류 조합이 100개가 넘을 때,
true를 반환한다. - 잘 보면 약간 이상한점이 있는데,
eldest라는 맵의Entry를 파라미터로 받아놓곤,size()를 활용한다.- 이 파라미터가 있는 이유는 이 메서드를 오버라이드할 때를 대비해서이다.
람다 인터페이스를 사용하여 비슷한 기능을 구현한다면?
@FunctionalInterface
interface EldestEntryRemovalFunction<K, V> {
boolean remove(Map<K, V> map, Map.Entry<K, V> eldest);
}
- 람다 인터페이스 를 이용해서 인터페이스를 작성하고 일반화할 수 있다.
- 위의
remove()메서드는 결국 두개의 인자를 받아boolean을 반환한다.- 이 경우 적합한 함수형 인터페이스인
BiPredicate가 존재한다.boolean을 반환하는 함수형 인터페이스는XxPredicate이니, 여기서 적합한거 찾아보면 된다.
- 이 경우 적합한 함수형 인터페이스인
표준 함수형 인터페이스를 사용한다면?
BiPredicate<Map<K, V>, Map.Entry<K, V>>
- 따로 인터페이스를 구성할 필요가 없어진다. (다만, 의미가 있는 이름의 인터페이스를 지어야만 한다면, 위의 방식이 나을 수도 있을 것 같다.)
java.util.function패키지에는 43개의 인터페이스가 담겨있다.
주요 6개의 함수형 인터페이스 기본형
UnaryOperator<T>,T apply(T t),String::toLowerCase- 인자 1개의 메서드로 파라미터로 들어온 타입을 그대로 반환한다.
BinaryOperator<T>,T apply(T t1, T t2),BigInteger::add- 인자 2개의 메서드로 파라미터로 들어온 타입을 그대로 반환한다.
Predicate<T>,boolean test(T t1),Collection::isEmpty- 인자 1개의 메서드로 파라미터로 들어온 타입을 이용해
boolean을 반환한다.
- 인자 1개의 메서드로 파라미터로 들어온 타입을 이용해
Function<T, R>,R apply(T t),Arrays::asList- 인자 1개의 메서드로 파라미터로 들어온 타입과 다른 타입을 반환한다.
Supplier<T>,T get(),Instant::now- 인자가 없는 메서드로, 값을 반환하기만 한다
Consumer<T>,void accept(T t),System.out::println- 인자 1개의 메서드로, 값을 소비하기만 하고 반환하지 않는다.
기본형 타입을 위한 함수형 인터페이스의 변형
- 기본형 타입은 굳이 박싱으로 인한 자원 낭비를 하지 않을 수 있도록 인터페이스를 따로 제공한다.
- ex)
IntPredicate는boolean test(int t1)과 같은 식이다. Function의 경우에는 3가지나 기본형 타입을 위한 인터페이스를 제공한다.IntToLongFunction: 둘 다 기본타입인 경우ToLongFunction: 하나만 기본타입인데, 반환 타입이long인 경우LongFunction: 하나만 기본타입인데, 파라미터의 타입이long인 경우
Bi...의 경우에도 위와 같이 복잡하게 보일 수 있는 기본형 전용 인터페이스들을 제공한다.
너무 복잡하다고 해서 박싱 타입을 사용하진 말자. 계산량이 많다면 엄청난 퍼포먼스 저하가 올 수 있다.
당연히 원하는 함수형 인터페이스가 없다면, 만드는 방식으로 개발을 진행해도 된다.
그냥 인터페이스와 함수형 인터페이스를 써야할 때 구분하기
- '반드시' 인터페이스를 함수형 인터페이스로 돌릴 필요는 없다.
Comparator<T>의 경우toIntBiFunction<T, U>로 변경하기 보단 독자적인 인터페이스로 남기는 것이 좋다.- API에서 자주 사용되는데, 이름이 역할을 훌륭하게 설명하기 때문이다.
- 구현하는 쪽에서 반드시 지켜야할 규약을 담고 있다.
- 유용한 디폴트 메서드들을 듬뿍 담고 있다.
위와 같은 사항을 충족한다면, 함수형 인터페이스를 따로 구현해야 하는지 충분히 고민하자.
함수형 인터페이스 전용 애너테이션
- 함수형 인터페이스를 작성할 때는
@FunctionalInterface애너테이션을 꼭 붙여주자.- 이 애너테이션을 쓰면 1개의 추상 메서드만 가져야 하는데, 다른 개발자의 실수로 여러 개의 추상 메서드가 생기면 에러를 발생시킨다.
함수형 인터페이스 API 사용 주의점
- 서로 다른 함수형 인터페이스를 같은 위치의 인수로 받는 메서드들을 다중으로 정의(오버로드)하지 말자.
- 보통 람다를 넘기기 때문에 강제 형변환이 필요할 수도 있고, 코드가 더러워진다.
핵심 정리
- 자바도 람다를 지원하니 API를 설계할 때 람다를 염두에 두자.
- 보통은
java.util.function의 표준 함수형 인터페이스를 쓰는 것이 좋다.- 프리미티브 타입을 위한 함수형 인터페이스 타입도 있다는 것을 기억하자.
- 그러나
Comparator<T>와 같은 경우는 함수형 인터페이스를 굳이 쓰지 않는 게 더 유용했다는 것도 기억하자.
'Java > 이펙티브 자바' 카테고리의 다른 글
| 이펙티브 자바, 쉽게 정리하기 - item 47. 반환 타입으로는 스트림보다 컬렉션이 낫다 (0) | 2023.03.31 |
|---|---|
| 이펙티브 자바, 쉽게 정리하기 - item 46. 스트림에서는 부작용 없는 함수를 사용하라 (0) | 2023.03.30 |
| 이펙티브 자바, 쉽게 정리하기 - item 45. 스트림은 주의해서 사용하라 (0) | 2023.03.29 |
| 이펙티브 자바, 쉽게 정리하기 - item 43. 람다보다는 메서드 참조를 사용하라 (2) | 2022.06.13 |
| 이펙티브 자바, 쉽게 정리하기 - item 42. 익명 클래스보다는 람다를 사용하라 (0) | 2022.06.13 |