본문 바로가기
Study/java

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

by 유경호 2021. 2. 8.
반응형

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

함수형 인터페이스는 Object 클래스의 메서드를 제외하고 추상 메소드를 하나만 가지고 있는 인터페이스이다. 다른 말로 SAM(Single Abstract Method) 인터페이스라고도 한다.

 

함수형 인터페이스 예시

@FunctionalInterface
public interface RunSomething {

    void doIt();

    static void printName() {
        System.out.println("Kyeongho");
    }

    default void printAge() {
        System.out.println("40");
    }
}
  • @FunctionalInterface 어노테이션을 붙이면 함수형 인터페이스라 간주하고 추상 메소드가 두 개 이상되면 컴파일 시점에 오류가 발생한다.
  • 자바8에서 인터페이스에 static 메소드 정의 가능, default 메소드 정의 가능
    → static 메소드, default 메소드가 몇 개나 존재하던 상관없이 추상 메소드가 하나이면 함수형 인터페이스이다.

함수형 인터페이스 사용 예시

public class App 
{
    public static void main( String[] args ) {
        // 익명 내부 클래스 anonymous inner class
      RunSomething runSomething = new RunSomething() {
                int baseNumber = 10;

                @Override
                public int doIt(int number) {
                    return number + 10;
                }
            };

            // 람다 표현식을 이용하여 코드를 간결하게
            RunSomething runSomthing_lambda = (number) -> number + 10;
            RunSomething runSomthing_lambda2 = (number) -> {
                System.out.println("Hello");
                System.out.println("Kyeongho");
                return number + 10;
            };
    }
}

자바에서 함수형 프로그래밍

함수형 인터페이스를 구현한 익명 객체는 결국 오브젝트다. 따라서 함수가 함수를 리턴하거나 함수가 함수를 파라미터를 받는등 다양한 함수형 프로그래밍이 가능하다. 다만 함수형 프로그래밍을 한다하면 다음과 같은 사항을 잘 준수하여야함. !특히 순수 함수를 주의하여 준수할 것

 

  • 함수를 First class object로 사용할 수 있다.
  • 순수 함수 (Pure function)
    • 사이드 이팩트가 없다. (함수 밖에 있는 값을 변경하지 않는다.)
    • 상태가 없다. (함수 밖에 있는 값을 사용하지 않는다.)
  • 고차 함수 (Higher-Order Function)
    • 함수가 함수를 매개변수로 받을 수 있고 함수를 리턴할 수도 있다.
  • 불변성

자바에서 제공하는 함수형 인터페이스

Java가 기본으로 제공하는 함수형 인터페이스

  • java.lang.funcation 패키지 → 자바에서 미리 정의해둔 자주 사용할만한 함수 인터페이스
  • Function
  • BiFunction
  • Consumer
  • Supplier
  • Predicate
  • UnaryOperator
  • BinaryOperator

좀 더 자세히 살펴보자.


Function<T, R>

  • T 타입을 받아서 R 타입을 리턴하는 함수 인터페이스
    • R apply(T t)
  • 함수 조합용 메소드
    • andThen
    • compose
@FunctionalInterface
  public interface Function<T, R> {
    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
      return t -> t;
    }
  }
  • compose: before 메서드 실행 후 결과를 받아서 현재 메소드 실행. 제네릭 타입은 처음 input과 마지막 output의 타입이다. 즉 ,메서드를 순서대로 실행시키기 위한 함수
  • andThen: compose와 반대로 현재 메소드를 실행 후 매게 변수로 받은 람다를 실행
  • identity: 자신의 값을 그대로 리턴하는 스테틱 메서드

ompose , andThen 차이점

andThen()과 compose()의 차이점은 어떤 함수형 인터페이스부터 먼저 처리하느냐의 차이가 있다.

인터페이스A.compose(인터페이스B);
B실행 -> A실행

인터페이스A.andThen(인터페이스B);
A실행 -> B실행

BiFunction<T,U,R>

  • 두 개의 값(T, U)를 받아서 R 타입을 리턴하는 함수 인터페이스
    • R apply(T t, U u)
  • 함수 조합용 메소드
    • andThen
@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);

    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
}
  • andThen: 현재 메소드를 실행 후 매게 변수로 받은 람다를 실행

Consumer<T>

  • T 타입을 받아서 아무값도 리턴하지 않는 함수 인터페이스
    • void Accept(T t)
  • 함수 조합용 메소드
    • andThen
@FunctionalInterface
  public interface Consumer<T> {
    void accept(T t);

     default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
  }
  • andThen: Consumer 인터페이스는 처리 결과를 리턴하지 않기 때문에 andThen() 디폴트 메서드는 함수형 인터페이스의 호출 순서만을 정한다.

Supplier<T>

  • T 타입의 값을 제공하는 함수 인터페이스
    • T get()
@FunctionalInterface
public interface Supplier<T> {
  T get();
}
  • get: 제네릭으로 전달받은 타입으로 값을 전달

Predicate<T>

  • T 타입을 받아서 boolean을 리턴하는 함수 인터페이스
    •  boolean test(T t)
  • 함수 조합용 메소드
    • And
    • Or
    • Negate
@FunctionalInterface
public interface Predicate<T> {
  boolean test(T t);

  default Predicate<T> and(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) && other.test(t);
  }

  default Predicate<T> negate() {
    return (t) -> !test(t);
  }

  default Predicate<T> or(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) || other.test(t);
  }

  static <T> Predicate<T> isEqual(Object targetRef) {
    return (null == targetRef)
      ? Objects::isNull
        : object -> targetRef.equals(object);
  }
}
  • and: 인자로 받는 다른 Predicate의 람다 수행 내용과 , 자기 자신의 람다 수행 내용을 &&연산
  • or: 인자로받는 다른 Predicate의 람다 수행내용과 , 자기자신의 람다 수행내용을 ||연산
  • negate: 람다 수행 내용을 ! 연산

UnaryOperator

  • Function의 특수한 형태로, 입력값 하나를 받아서 동일한 타입을 리턴하는 함수
    인터페이스

BinaryOperator

  • BiFunction의 특수한 형태로, 동일한 타입의 입렵값 두개를 받아 리턴하는 함수
    인터페이스

 


참고

Baeldung - www.baeldung.com/java-8-functional-interfaces

반응형