[JAVA] Java에서 제공하는 함수형 인터페이스 -(3)

윤재열·2022년 10월 31일
1

Java

목록 보기
62/71
post-custom-banner

Java 에서 제공하는 함수형 인터페이스 4가지

-Supplier<T>
-Consumer<T>
-Function<T,R>
-Predicate<T>

1. Supplier<T>

  • Supplier 는 매개변수 없이 반환값 만을 갖는 함수형 인터페이스 입니다.
  • Supplier는 T get()을 추상메서드로 갖고 있습니다.
// 정의
@FunctionallInterface
public interface Supplier<T>{
	T get();
}

// 사용 예시
Supplier<String> supplier = () -> "Hello World";
System.out.println(supplier.get());

2. Consumer<T>

  • Consumner는 객체 T를 매개변수로 받아서 사용되며, 반환값은 없는 함수형 인터페이스 입니다.
  • Consumer는 void accept(T t)를 추상메서드로 갖습니다.
  • 또한 Consumer는 andThen 이라는 함수를 제공하고 있는데, 이를 통해 하나의 함수가 끝난 후 다음 Consumer는 연쇄적으로 사용할 수 있습니다.
  • 아래의 예제에서는 먼저 accept로 받아들인 Consumer를 먼저 처리하고, andThen으로 받은 두번째 Consumer를 처리하고 있습니다.
  • 함수형에서 함수는 값의 대입 또는 변경 등이 없기 때문에 Consumer가 split으로 데이터를 변경하였다 하더라도 원본의 데이터는 유지됩니다.
// 정의
@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<String> consumer = (str) -> System.out.println(str.split(" ")[0]);
consumer.andThen(System.out::println).accept("Hello World");

// 출력
Hello
Hello World

Function<T,R>

  • Function은 객체 T를 매개변수로 받아서 처리한 후 R로 반환하는 함수형 인터페이스 입니다.
  • Function은 R apply(T t)를 추상메서드로 갖습니다.
  • 또한 Function은 Consumer와 마찬가지로 andThen을 제공하고 있으며, 추가적으로 compose를 제공하고 있습니다.
  • Consumer에서 andThen은 첫번째 함수가 실행된 이후에 다음 함수를 연쇄적으로 실행하도록 연결하여 준다고 했지만 compose는 첫번째 함수 실행 이전에 먼저 함수를 실행하여 연쇄적으로 연결해주다는 차이가 있습니다.
  • 또한 identity 함수가 존재하는데, 이는 자기 자신을 반환하는 static 함수 입니다.
// 정의
@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;
    }
}

// 예시, 메소드 참조로 간소화 가능(String::length;)
Function<String, Integer> function = str -> str.length();
function.apply("Hello World");

Predicate<T>

  • Predicate는 객체 T를 매개 변수로 받아 처리한 후 Boolean을 반환합니다.
  • Predicate는 Boolean test(T t)를 추상 메서드로 갖고 있습니다.
// 정의
@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<String> predicate = (str) -> str.equals("Hello World");
predicate.test("Hello World");

메서드 참조(Method Reference)

  • 메서드 참조란 함수형 인터페이스를 람다식이 아닌 일반 메서드를 참조시켜 선언하는 방법 입니다.

    • 함수형 인터페이스의 매개변수 타입 = 메서드의 매개변수 타입
    • 함수형 인터페이스의 매개변수 개수 = 메서드의 매개변수 개수
    • 함수형 인터페이스의 반환형 = 메서드의 반환형
  • 참고가능한 메서드는 일반 메서드, static 메서드, 생성자가 있으며 클래스이름::메서드이름 으로 참조할 수 있습니다.

일반 메서드 참조

  • 예를 들어서 위에서 Function에 메서드 참조를 적용하려고 합니다.
  • 우선 해당 메서드의(length)가 위의 3가지 조건을 만족하는지 살펴 보아야 합니다.
    • 매개변수 없음
    • 매개변수 개수 = 0개
    • 반환형 = int
  • String의 length 함수는 매개변수가 없으며, 반환형이 int로 동일하기 때문에 String::length로 다음과 같이 메서드 참조를 적용할 수 있습니다.
// 기존의 람다식
Function<String, Integer> function = (str) -> str.length();
function.apply("Hello World");

// 메소드 참조로 변경
Function<String, Integer> function = String::length;
function.apply("Hello World");
  • 추가로 System.out.println() 메서드는 반환형이 void 이며, 파라미터로 string을 받는 메서드입니다.
  • 그렇기 때문에 우리는 Consumer에 System.out.println() 메서드를 참조 시킬수 있습니다.
// 일반 메소드를 참조하여 Consumer를 선언한다.
Consumer<String> consumer = System.out::println;
consumer.accept("Hello World!!");

// 메소드 참조를 통해 Consumer를 매개변수로 받는 forEach를 쉽게 사용할 수 있다.
List<String> list = Arrays.asList("red", "orange", "yellow", "green", "blue");
list.forEach(System.out::println);

//interface Iterable<T>
default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}

Static 메서드 참조

  • static 메서드 역시 메서드 참조가 가능합니다.
  • 예를들어 Objects의 isNull은 반환값이 Boolean이며,
    매개변수 값은 1개이고, 매개변수가 Object이므로 Predicate로 다음과 같이 메서드 참조가 가능합니다.
Predicate<Boolean> predicate = Objects::isNull;

// isNull 함수
public static boolean isNull(Object obj) {
    return obj == null;
}

생성자 참조

  • 생성자도 메서드 참조를 할 수 있습니다.
  • 생성자는 new로 생성해주므로 클래시이름 ::new로 참조할수 있습니다. Supplier는 매개변수가 없이 반환값만을 갖는 인터페이스이기 때문에, 매개변수 없이 String객체를 새롭게 생성하는 String의 생성자를 참조하여 Supplier로 선언할 수 있습니다.
Supplier<String> supplier = String::new;
출처: https://mangkyu.tistory.com/113 [MangKyu's Diary:티스토리]
profile
블로그 이전합니다! https://jyyoun1022.tistory.com/
post-custom-banner

0개의 댓글