본문 바로가기
JAVA/[JAVA] Stream

Functional Interface (1)

by oncerun 2022. 2. 7.
반응형

 

자바는 여전히 OOP를 지원하는 명령형 프로그래밍 언어지만 흐름에 맞게 함수형 프로그래밍도 지원한다.

 

선언형 프로그래밍은 무엇을 해야 하는지에 따라 문제를 해결하는 방식으로 기존 명령형 프로그래밍과 다른 문제 해결 방식을 보인다. 

 

이러한 선언형 프로그래밍으로 문제를 해결하는 방법을 공부하기 위해 자바 8에서 지원하는 java.util.function 패키지의 인터페이스를 공부해보고 명령형으로 처리하면 가독성이 떨어지고 확장성이 떨어지는 문제들을 선언형 프로그래밍으로 언제든 구현할 수 있도록 준비해보자. 

 

선언형 프로그래밍을 공부하는 과정에서 JS에 관한 지식이 있다면 한결 부드럽게 다가갈 수 있다. 자바스크립트의 함수는 1급 시민으로 함수를 변수에 할당할 수 있으며, 함수를 매개변수로 넘겨줄 수 있고, 리턴 값으로 함수를 리턴할 수 있다.

 

이는 1급 시민의 조건으로 선언형 프로그래밍의 토대가 된다. 자바에서는 이를 어떻게 지원했을까? 

자바에서는 interface라는 규약을 통해 구현체를 구현할 토대를 생성할 수 있다. 이를 활용하면 함수형 프로그래밍을 할 수 있는데 java.util.function에 존재하는 Function 인터페이스를 구현해 간단한 예제를 작성해보자.

 

package java.util.function;

import java.util.Objects;

/**
 * Represents a function that accepts one argument and produces a result.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #apply(Object)}.
 *
 * @param <T> the type of the input to the function
 * @param <R> the type of the result of the function
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    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;
    }
}

 

* @FunctionalInterface 어노테이션은 단 하나의 추상 메서드를 가지는 인터페이스에 붙일 수 있다. 

Function Interface에는 apply라는 추상 메서드가 존재하며 리턴 타입은 R, 파라미터는 T 타입으로 T를 입력받아 R을 리턴하는 하나의 함수(메서드)이다.

 

 

Function을 implements 한 Adder Class를 생성하고 apply를 오버라이드 하여 실 구현체를 생성했다. 

public class Adder implements Function<Integer, Integer> {
    @Override
    public Integer apply(Integer x) {
        return x +10;
    }
}

Function의 첫 번째  파라미터 타입인 Input타입을 지정했고 output타입으로 Interger를 두 번째 파라미터로 지정했다. 

public static void main(String[] args) {
    Function<Integer, Integer> adderObj = new Adder();
    Integer result = adderObj.apply(10);
    log.info("result {}", result);
}

 

이 방법에는 하나의 치명적인 단점이 존재한다. 하나의 함수를 추가하기 위해 하나의 클래스를 생성해야 하는 문제가 있다.

 

이를 해결하기 위해 익명 함수, Lambda Expression가 존재한다. 

이 람다 표현식에는 다음과 같은 특징이 존재한다.

  • 매개변수의 타입이 유추가능할 경우 타입 생략이 가능하다.
  • 매개변수가 하나일 경우 괄호를 생략할 수 있다.
  • 함수의 몸체에서 바로 리턴하는 경우 중괄호를 생략할 수 있다.

 

람다식을 이용해 위 코드를 리팩터링 해보자.

public static void main(String[] args) {

    Function<Integer, Integer> adderObj = x -> x + 10;

    Integer result = adderObj.apply(10);

    log.info("result {}", result);
}

 

Function <Interger, Interger>라는 제네릭 타입으로 매개변수의 타입을 유추할 수 있어 매개변수 타입을 생략했다. 

또한 하나의 매개변수인 경우 소괄호도 생략이 가능하다. 이후 바로 리턴하기 때문에 중괄호를 생략했다. 

 

생략하지 않은 코드를 보자.

Function<Integer, Integer> adderObj = (Integer x) -> {
    return x + 10;
};

 

하나의 입력과 하나의 리턴을 하는 함수를 생성해 보았는데, 2개, 3개의 입력을 받을 경우 어떻게 처리하는지 생각해보자.

 

java.util.function.BiFunction 인터페이스는 2개의 파라미터를 받아 하나의 리턴 값을 처리하도록 미리 구현되어 있다. 이를 활용해 2개의 인수를 받는 예제를 진행하자.

 

java.util.function.BiFunction<Integer, Integer, Integer> add = (x, y) -> x +y;
Integer result = add.apply(10, 20);

log.info("result : {}", result);

별도의 클래스는 필요 없다. 람다식을 통해  간단히 만들면 된다.

 

 

3개의 인자를 받는 인터페이스는 지원하지 않지만 우리가 인터페이스를 만들면 된다. 

 

package com.example.functional.utill;

@FunctionalInterface
public interface TriFunctionInterface<T, U, V, R> {
    R apply(T t, U u, V v);
}

 

TriFunctionInterface는 T, U, V타입의 3개의 인자를 받아 R타입으로 리턴한다.


TriFunctionInterface<Integer,Integer,Integer,Integer> addThreeNum = (x, y, z) ->  x+y+z;
Integer result = addThreeNum.apply(10, 20, 30);
log.info(" result : {}",result);

 

 

 

[마무리]

 

간단하게 함수형 프로그래밍을 자바가 어떻게 지원하는지, 사용은 어떻게 하는지, 람다식을 어떻게 활용하고 생략 가능한 경우는 무엇인지를 알아보았다. 

다음에는 Stream을 사용하기 위한 여러 인터페이스를 공부해볼 것이다.

반응형

'JAVA > [JAVA] Stream' 카테고리의 다른 글

Stream (3)  (0) 2022.02.16
Stream (2)  (0) 2022.02.15
Stream  (0) 2022.02.12
Method Reference  (0) 2022.02.09
Functional Interface (2)  (0) 2022.02.08

댓글