개발 기록
[이펙티브 자바] 아이템 44 본문
## 표준 함수형 인터페이스를 사용하라
java,util.function 패키지를 보면 다양한 용도의 표준 함수형 인터페이스가 담겨있다.
필요한 용도에 맞는 게 있다면, 직접 구현하지 말고 표준 함수형 인터페이스를 활용하라.
- API가 다루는 개념의 수가 줄어들어 익히기 더 쉬워짐
- 표준 함수형 인터페이스들은 유용한 디폴트 메서드를 많이 제공하므로 다른 코드와의 상호운용성도 크게 좋아짐
- 매개타입으로 사용되어 람다식을 매개값으로 대입할 수 있도록 해준다.
### 기본 인터페이스 6개 (표준 함수형 인터페이스는 총 43개)
- Operator 인터페이스
반환값과 인수의 타입이 같은 함수
인수가 1개인 UnaryOperator와 2개인 BinaryOperator가 있다. - Predicate 인터페이스
인수 하나를 받아 boolean을 반환하는 함수 - Funtion 인터페이스
인수와 반환 타입이 다른 함수 - Supplier 인터페이스
인수를 받지 않고 값을 반환(혹은 제공)하는 함수 - Consumer 인터페이스
인수를 하나 받고 반환값은 없는(특히 인수를 소비하는) 함수
인터페이스 | 함수 시그니처 | 예 |
UnaryOperator<T> | T apply(T t) | String::toLowerCase |
BinaryOperator<T> | T apply(T t1, T t2) | BigInteger::add |
Predicate<T> | boolean test(T t) | Collection::isEmpty |
Function<T, R> | R apply(T t) | Arrays::asList |
Supplier<T> | T get() | Instant::now |
Consumer<T> | void accept(T t) | System.out::println |
// 객체 T를 받아 출력하는 함수 정의
Consumer<String> c1 = t -> System.out.println("입력값 : "+ t);
c1.accept("홍길동");
list.forEach(s -> System.out.println(s)); // Consumer<String> 사용
** 표준 함수형 인터페이스는 대부분 기본 타입만 지원한다. 그렇다고 기본 함수형 인터페이스에 박싱 된 기본 타입을 넣어 사용하지는 말자. 계산량이 많을 때 성능이 매우 느려질 수 있기 때문이다.
### 아래의 3가지 경우 중 하나 이상을 만족한다면 전용 함수형 인터페이스 구현을 고민해 보자.
- 자주 쓰이며, 이름 자체가 용도를 명확히 설명해 준다. ( 용도를 명확히 하고 코드 가독성 향상 )
- 반드시 따라야 하는 규약이 있다. ( 특정한 사용 방식이나 제약 조건을 강제하고 싶을 때 )
- 유용한 디폴트 메서드를 제공할 수 있다. ( 조합 가능한 공통 기능 추가 )
### @FunctionalInterface 애너테이션
- @Override처럼 프로그래머의 의도를 명시하는 역할
- 해당 클래스의 코드나 설명 문서를 읽을 이에게 그 인터페이스가 람다용으로 설계된 것임을 알려준다.
- 해당 인터페이스가 추상 메서드를 오직 하나만 가지고 있어야 컴파일되게 해준다.
- 유지보수 과정에서 누군가 실수로 메서드를 추가하지 못하도록 해준다.
### 주의점
- 서로 다른 함수형 인터페이스를 같은 위치의 인수로 받는 메서드들을 다중 정의해서는 안 된다
( 함수형 인터페이스끼리는 서로 모호하게 겹칠 수 있어서 오버로딩하면 문제가 생긴다 )
public interface ExecutorService extends Executor {
...
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
...
}
ExecutorService의 submit 메서드는 Callable<T>를 받는 것과 Runnable을 받는 것을 다중정의했다.
그 결과, 올바른 메서드를 알려주기 위해 형변환을 해야 할 때가 매우 많이 생기게 된다.
ex. )
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> future = executor.submit(() -> {
System.out.println("Running a task");
return 42;
}); // 어떤 submit이 호출됐을까?
Future<Integer> future = executor.submit((Callable<Integer>) () -> {
System.out.println("Running a task");
return 42;
});
## 핵심 정리
이제 자바에서는 람다를 사용할 수 있으므로,메서드 설계 시 입력값과 반환값에 함수형 인터페이스 타입을 활용하라.
가능하면 java.util.function의 표준 함수형 인터페이스를 쓰는 것이 좋지만 직접 만들어 쓰는 편이 나은 경우도 있다.
'JAVA' 카테고리의 다른 글
[이펙티브 자바] 아이템 57 (0) | 2025.05.06 |
---|---|
[이펙티브 자바] 아이템 48 (0) | 2025.05.06 |
[이펙티브 자바] 아이템 42 (0) | 2025.03.18 |
[이펙티브 자바] 아이템 40 (0) | 2025.03.11 |
[이펙티브 자바] 아이템 37 (0) | 2025.02.18 |