이펙티브 자바, 쉽게 정리하기 - 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 |