-
아이템44. 표준 함수형 인터페이스를 사용하라JAVA/Effective java 2021. 5. 9. 15:55
- 자바가 람다를 지원하며 API를 작성하는 모범 사례도 크게 바뀌었다.
- 상위 클래스의 기본 메서드를 재정의해 원하는 동작을 구현하는 템플릿 메서드 패턴의 매력이 크게 줄었다.
- 이를 대체하는 현대적 해법은 같은 효과의 함수 객체를 받는 정적 팩터리나 생성자를 제공하는 것이다.
- 이 내용을 일반화하면 함수 객체를 매개변수로 받는 생성자와 메서드를 더 많이 만들어야 한다.
- 자바 표준 라이브러리에는 같은 모양의 인터페이스가 준비되어 있다.
- java.util.function 패키지를 보면 다양한 용도의 표준 함수형 인터페이스가 담겨있다.
- 필요한 용도에 맞는게 있다면, 직접 구현하지 말고 표준 함수형 인터페이스를 활용하자.
- java.util.function의 총 43개의 인터페이스 중 기본 인터페이스 6개만 기억한다면 나머지를 충분히 유추해낼 수 있는데 하나씩 살펴보자.
- 이 기본 인터페이스들은 모두 참조 타입용이다.
- Operator: 인수가 1개인 UnaryOperator와 2개인 BinaryOperator로 나뉘며, 반환값과 인수의 타입이 같은 함수
- Predicate: 인수 하나를 받아 boolean을 반환하는 함수
- Function : 인수와 반환 타입이 다른 함수
- Supplier: 인수를 받지 않고 값을 반환하는 함수
- Consumer: 인수를 받고 반환값은 없는 함수
Operator
- 기본 인터페이스는 기본 타입인 int,long,double 용으로 각 3개씩 변형이 생겨난다.
- 그 이름도 기본 인터페이스의 이름 앞에 해당 기본 타입 이름을 붙여 지었다.
- 예컨대 int를 받는 Predicate은 IntPredicate가 되고 long을 받아 long을 반환하는 BinaryOperator는 LongBinaryOperator가 되는 식이다.
SrcToResult, ToResult
- Function 인터페이스는 기본 타입을 변환하는 변형이 총 9개가 더 있다.
- 인수와 같은 타입을 반환하는 함수는 UnaryOperator이므로, Function 인터페이스의 변형은 입력과 결과의 타입이 항상 다르다.
- 입력과 결과 타입이 모두 기본 타입이면 접두어로 SrcToResult를 사용한다.
- 예를 들어 long을 받아 int를 반환하면 LongToIntFunction이 되는 식이다.
- 나머지는 입력이 객체 참조익 결과가 int, long, double인 변형들로, ToResult를 사용할 수도 있다.
- 즉, ToLongFunction<int[]> 는 int[] 인수를 받아 long을 반환한다.
변형
- 인수를 2개씩 받는 변형의 예로, BiPredicate<T,U>, BiFunction<T,U,> 가 있다.
- 또 BiFunction에는 다시 기본 타입을 반환하는 세 변형 ToIntBiFunction<T,U>, ToLongBiFunction<T,U>, ToDouble.. 등이 존재한다.
- Consumer에도 객체 참조와 기본 타입 하나, 즉 인수를 2개 받는 변형인 ObjDoubleConsumer,T> 등이 존재한다.
- 표준 함수형 인터페이스 대부분은 기본 타입만 지원한다.
- 그렇다고 기본 함수형 인터페이스에 박싱된 기본 타입을 넣어 사용하지는 말자.
- 계산량이 많을 때는 성능이 느려질 수 있다.
- 표준 인터페이스 중 필요한 용도에 맞는 게 없다면 직접 작성해야 한다.
- 자주 보아온 Comparator<T> 인터페이스를 떠올려보자. 구조적으로는 ToInt, BiFunction<T,U>와 동일하다.
- 하지만 다음의 특성으로 독자적인 인터페이스로 살아남는데 이 중 하나 이상을 만족한다면 전용 함수형 인터페이스를 구현해야 하는 건 아닌지 진중히 고민해야 한다.
- 자주 쓰이며 , 이름 자체가 용도를 명확히 설명해준다.
- 반드시 따라야 하는 규약이 있다.
- 유용한 디폴트 메서드를 제공할 수 있다.
@FunctionalInterface
- 이 애너테이션을 사용하는 이유는 @Override를 사용하는 이유와 비슷하다.
- 프로그래머의 의도를 명시하는 것으로, 크게 세가지 목적이 있다.
- 첫 째, 해당 클래스의 코드나 설명 문서를 읽을 이에게 그 인터페이스가 람다용으로 설계된 것임을 알려준다.
- 두 번째, 해당 인터페이스가 추상 메서드를 오직 하나만 가지고 있어야 컴파일되게 해준다.
- 세 번째, 그 결과 유지보수 과정에서 누군가 실수로 메서드를 추가하지 못하게 막아준다.
- 따라서 직접 만든 함수형 인터페이스에는 항상 @FunctionalInterface 애너테이션을 사용하자.
핵심 정리
- API를 설계할 때 람다도 염두에 두어야 한다.
- 입력값과 반환값에 함수형 인터페이스 타입을 활용하라.
- 보통은 java.util.function 패키지의 표준 함수형 인터페이스를 사용하는 것이 가장 좋은 선택이다..
- 흔치는 않지만 직접 새로운 함수형 인터페이스를 만들어 쓰는 편이 나을 수도 있음을 잊지 말자.
참고 자료
'JAVA > Effective java' 카테고리의 다른 글
아이템43. 람다보다는 메서드 참조를 사용하라 (0) 2021.05.08 아이템42. 익명 클래스보다는 람다를 사용하라 (0) 2021.05.05 아이템41. 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라. (0) 2021.05.04 아이템 40. @Override 애너테이션을 일관되게 사용하라 (0) 2021.04.18 아이템39. 명명 패턴보다 에너테이션을 사용하라 (0) 2021.04.16