JAVA

java.util.function패키지가 제공하는 FunctionalInterface정리

바디스 2023. 2. 9. 14:57

람다식을 사용할 대마다 함수형 인터페이스를 매번 정의하기 불편해서 아예 라이브러리로 제공하는 것들이 있습니다.

자바8에서 제공하는 주요 Funcational 인터페이스는 java.util.function 패키지에 다음과 같이 있습니다.

  • Function
  • Supplier
  • Consumer
  • Predicate
  • Operators

Function

가장 기본적인 형태로 Object를 입력받고 Object를 리턴하는 매소드 입니다. <T> 는 매개변수의 타입이고, <R> 은 리턴 타입입니다.
호출하는 함수는 apply 입니다.

@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;
  }
}

사용 예시

Function<Integer, Integer> square = x -> x * x;
System.out.println(square.apply(5)); // Outputs 25

Function 에는 compose라는 메소드가 있어 메소드를 순서대로 실행시킬 수 있습니다.

Function<Integer, String> intToString = Objects::toString;
Function<String, String> quote = s -> "'" + s + "'";

Function<Integer, String> quoteIntToString = quote.compose(intToString);
System.out.println(quoteIntToString.apply(5)); // '5'

addThencompose와 반대역할로 현재 메소드를 실행 후 매개변수로 받은 람다를 실행합니다.

Function<String, String> upperCase = v -> v.toUpperCase();

String result = upperCase.andThen(s -> s + "abc").apply("a");
System.out.println(result); // Aabc

identity는 자신의 값을 그대로 리턴합니다.

String abc = Function.identity().apply("abc");
System.out.println(abc); // abc

Supplier

get() 메서드로 generic으로 선언된 타입을 리턴하는 인터페이스, 단순한 공급자로 추가적인 메서드는 없습니다.

@FunctionalInterface
public interface Supplier<T> {
  T get();
}

사용 예시

Supplier<Double> randomNumber = () -> Math.random();
System.out.println(randomNumber.get());

Consumer

Supplier와 반대로 입력은 받지만 리턴하지 않는 인터페이스입니다. accept()매서드로 입력 받은 인수로 작업을 실행합니다.

@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); };
  }
}

사용 예시

Consumer<Integer> print = x -> System.out.println(x);
print.accept(5); // Outputs 5

Predicate

특정 타입의 매개변수를 받아 boolean값을 리턴하는 인터페이스입니다.

@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);
  }
}

사용 예시

Predicate<Integer> isEven = x -> x % 2 == 0;
System.out.println(isEven.test(4)); // Outputs true
System.out.println(isEven.test(5)); // Outputs false

Operators

Operators는 매개변수를 받고 동일한 타입을 리턴하는 인터페이스로 Function을 상속하고 있습니다.

@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
  static <T> UnaryOperator<T> identity() {
    return t -> t;
  }
}

사용 예시

UnaryOperator<String> toLower = (s)-> s.toLowerCase();
System.out.println(toLower.apply("Hello World")); // "hello world"

그외의 함수들

매개변수에 따라 구분해서 사용 할 수 있습니다.

Supplier, Consumer, Predicate
  • 앞에 Int, Long, Double 등을 붙여서 사용합니다.
    IntSupplier
    LongSupplier
    DoubleSupplier
    IntConsumer
    LongConsumer
    DoubleConsumer
    intPredicate
    DoublePredicate
    LongPredicate
Function의 경우
  • 리턴타입을 제니릭으로 받는 경우 앞에 Int, Long, Doulbe을 붙이고
  • 매개변수 타입을 제네릭으로 받는 경우 이걸로 리턴한다는 뜻이 To를 추가하여 ToInt, ToLong, ToDouble을 붙입니다.
    IntFunction
    LongFunction
    DoubleFunction
    ToIntFunction
    ToLongFunction
    ToDoubleFunction

매개변수가 두개인 Bi 버전도 있습니다.

BiConsumer
BiFunction
BinaryOperator
BiPredicate

ObjIntConsumer
ObjLongConsumer
ObjDoubleConsumer