개발 기록

[이펙티브 자바] 아이템 44 본문

JAVA

[이펙티브 자바] 아이템 44

수염차 2025. 4. 8. 21:26

## 표준 함수형 인터페이스를 사용하라

 

java,util.function 패키지를 보면 다양한 용도의 표준 함수형 인터페이스가 담겨있다. 

필요한 용도에 맞는 게 있다면, 직접 구현하지 말고 표준 함수형 인터페이스를 활용하라.

- API가 다루는 개념의 수가 줄어들어 익히기 더 쉬워짐

- 표준 함수형 인터페이스들은 유용한 디폴트 메서드를 많이 제공하므로 다른 코드와의 상호운용성도 크게 좋아짐

- 매개타입으로 사용되어 람다식을 매개값으로 대입할 수 있도록 해준다.

 

### 기본 인터페이스 6개 (표준 함수형 인터페이스는 총 43개)

  1. Operator 인터페이스
    반환값과 인수의 타입이 같은 함수
    인수가 1개인 UnaryOperator와 2개인 BinaryOperator가 있다.
  2. Predicate 인터페이스
    인수 하나를 받아 boolean을 반환하는 함수
  3. Funtion 인터페이스
    인수와 반환 타입이 다른 함수
  4. Supplier 인터페이스
    인수를 받지 않고 값을 반환(혹은 제공)하는 함수
  5. 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가지 경우 중 하나 이상을 만족한다면 전용 함수형 인터페이스 구현을 고민해 보자.

  1. 자주 쓰이며, 이름 자체가 용도를 명확히 설명해 준다. ( 용도를 명확히 하고 코드 가독성 향상 )
  2. 반드시 따라야 하는 규약이 있다. ( 특정한 사용 방식이나 제약 조건을 강제하고 싶을 때 )
  3. 유용한 디폴트 메서드를 제공할 수 있다. ( 조합 가능한 공통 기능 추가 )

### @FunctionalInterface 애너테이션

- @Override처럼 프로그래머의 의도를 명시하는 역할

  1. 해당 클래스의 코드나 설명 문서를 읽을 이에게 그 인터페이스가 람다용으로 설계된 것임을 알려준다.
  2. 해당 인터페이스가 추상 메서드를 오직 하나만 가지고 있어야 컴파일되게 해준다.
  3. 유지보수 과정에서 누군가 실수로 메서드를 추가하지 못하도록 해준다.

### 주의점

- 서로 다른 함수형 인터페이스를 같은 위치의 인수로 받는 메서드들을 다중 정의해서는 안 된다

( 함수형 인터페이스끼리는 서로 모호하게 겹칠 수 있어서 오버로딩하면 문제가 생긴다 )

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
Comments