Study/java

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

유경호 2021. 2. 8. 16:49
반응형

함수형 인터페이스(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

반응형