콘텐츠로 이동

Functional Interface

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

자바가 람다를 지원하게 되면서 함수 객체를 받는 메서드나 생성자를 작성할 수 있게 되었다.
함수형 인터페이스를 직접 구현할 수도 있지만 이미 자바는 표준 API로 java.util.function 패키지에 있는 함수형 인터페이스를 사용하는 것을 권장한다.

  • java.util.function 패키지의 기본 인터페이스 6가지
인터페이스함수 시그니처예시설명
UnaryOperatorT apply(T t)String::toLowerCase인수(1개)와 반환값의 타입이 같은 함수
BinaryOperatorT apply(T t1, T t2)BigInteger::add인수(2개)와 반환값의 타입이 같은 함수
Predicateboolean test(T t)Collection::isEmpty인수를 받아 boolean을 반환하는 함수
Function<T, R>R apply(T t)Arrays::asList인수와 반환값의 타입이 다른 함수
SupplierT get()Instant::now인수를 받지 않고 값을 반환하는 함수
Consumervoid accept(T t)System.out::println인수를 하나 받고 반환값 없이 인수를 소비하는 함수

모든 기본 인터페이스는 기본 타입인 int, long, double을 각각 받는 변형 인터페이스 3개씩 제공하게 되고, 그 외에 다양한 변형 인터페이스도 제공하고 있다.
(총 43개지만, 규칙성이 부족하기 때문에 필요할 때 찾아 사용하는 것이 좋다.)

많은 수의 인터페이스를 지원하기 때문에 대부분 상황에서는 표준 함수형 인터페이스를 사용하고, 없는 경우에만 직접 구현하는 것이 좋다.

표준 함수형 인터페이스를 사용하지 않는 경우

섹션 제목: “표준 함수형 인터페이스를 사용하지 않는 경우”

이미 똑같은 시그니처를 갖는 인터페이스가 존재하고 있음에도 불구하고, 사용하지 않고 별도의 인터페이스를 만들어 사용하는 경우가 있다.
그 예시는 Comparator<T>ToIntBiFunction<T, U>로, 둘이 구조적으로 동일한 역할을 하지만 Comparator를 독자적인 인터페이스로 사용하는 것이 일반적이다.

  1. 자주 쓰이면서, 이름 자체가 용도를 명확하게 설명해줌
  2. 구현하는 쪽에서 지켜야 할 규약이 명확하게 정의되어 있음
  3. 비교자들을 변환하고 조합할 수 있는 유용한 메서드들을 제공함

만약 위의 내용 중 하나 이상을 만족한다면 표준 함수형 인터페이스가 아닌 전용 함수형 인터페이스를 구현하는 것도 좋은 방법이 될 수 있다.(구현할 땐 @FunctionalInterface 어노테이션을 사용하자)

함수형 인터페이스 사용시 주의점

섹션 제목: “함수형 인터페이스 사용시 주의점”

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

public interface ExecutorService extends Executor {
// ...
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
// ...
}

이렇게 되면 클라이언트에선 올바른 메서드 사용을 하기 위해 형변환을 해야하기 때문에, 이는 피하는 것이 좋다.

class Main {
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
exec.submit(() -> System.out.println("Hello")); // 어떤 것이 호출 되는지 모호함
// 형변환을 통해 명시적으로 어떤 것을 호출할 것인지 명시할 수 있음
exec.submit((Callable<Void>) () -> {
System.out.println("Hello");
return null;
});
}
}

마지막 업데이트:

Java