Programming Language/Java

Java의 함수형 인터페이스(Functional Interface)에 대해 알아보기

TwinParadox 2021. 5. 17. 00:15
728x90

함수형 인터페이스(Functional Interface)?

  • 하나의 추상 메서드만 존재하는 인터페이스
  • 람다 표현식에 사용할 수 있는 인터페이스
  • 인터페이스 범위가 확장됨에 따라 여러 메서드가 정의되어 있을 수 있지만, 오직 하나의 추상 메서드만 존재한다면 함수형 인터페이스이다.

 

기본 형태

@FunctionalInterface
public interface Consumer<T> {
    void accept(T var1);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (t) -> {
            this.accept(t);
            after.accept(t);
        };
    }
}

@FunctionalInterface 어노테이션은 생략이 가능하다. 다만, 이를 명시하여 함수형 인터페이스임을 명확하게 할 수 있고, 개발자가 메서드를 추가하게 되면 컴파일 에러로 이를 방지할 수 있다.

 

 

함수형 인터페이스로는 무엇이 있는가?

  • Runnable, Comparator, RowMapper(Spring Framework) 등
  • 인터페이스에 @FunctionalInterface가 붙어 있는 라이브러리들

 

 

람다 표현식과는 무슨 관계인가?

  • 람다식을 사용하기 위해서는 인터페이스에 public 메서드 하나만 있어야 한다. 따라서, 함수형 인터페이스 규칙을 따르는 인터페이스는 람다식을 활용할 수 있는 인터페이스이다.
  • 이런 인터페이스는 람다식을 해당 메서드의 파라미터와 형식에 맞는지 검사 후 맞으면 컴파일하며, 명세가 다를 경우 컴파일 에러를 발생시킨다.
  • 설령 Java8 이전에 만들어진 인터페이스라고 하더라도 오직 하나의 추상 메서드를 갖는 인터페이스라면 람다식을 활용할 수 있다.

 

java.util.function을 통해 알아보는 함수형 인터페이스

  • 아래 인터페이스들은 Java8부터 나오기 시작한 라이브러리, 프레임워크에 적극적으로 활용되고 있기 때문에 각 유형에 대해 숙지하고 있는 것이 좋다.
  • 기본 제공하는 함수형 인터페이스는 추가 작업이 필요 없다.
  • 기본 인터페이스가 프로젝트의 명명 규칙과 맞지 않을 때는 사전에 함수형 인터페이스를 정의하고 가는 방법도 고려하는 것이 좋다.

 

Consumer<T>

void accept(T t)

파라미터를 전달해 처리하고, 리턴 받지 않을 경우 사용

 

Function<T, R>

R apply(T, t)

전달할 파라미터를 다른 값으로 변환/매핑하여 리턴할 때 사용

 

Predicate<T>

boolean test(T t)

파라미터에 대해 True/False를 리턴할 때(필터링, 검증) 사용

 

Supplier<T>

T get()

파라미터 없이 리턴 값만 있는 경우에 사용

 

Operator 인터페이스

  • 앞서 언급된 4개 인터페이스 제외하고도 Operator 인터페이스도 존재한다. 
  • Operator 인터페이스는 모두 Function의 하위 인터페이스이며, Function의 apply 메서드를 사용한다. 따라서, Operator 인터페이스에서 추가되는 함수형 메서드는 없다.
  • 연산을 몇 개 가지냐에 따라 인터페이스는 Unary/Binary 계열로 분리된다.
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
    static <T> UnaryOperator<T> identity() {
        return (t) -> {
            return t;
        };
    }
}
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T, T, T> {
    static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> {
            return comparator.compare(a, b) <= 0 ? a : b;
        };
    }

    static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> {
            return comparator.compare(a, b) >= 0 ? a : b;
        };
    }
}

 

 

java.util.function의 기본 활용 예시

public class FunctionExample {
    public static int executeFunction(String context, Function<String, Integer> function) {
        // Function은 가장 대표적인 Functional Interface

        // T, R
        // T : Parameter
        // R : Return
        return function.apply(context);
    }

    public static void main(String[] args) {
        FunctionExample.executeFunction("Hello! Lambda & Functional Interface",
                (String context) -> context.length());
    }
}

 

728x90