함수형 프로그래밍에서는 함수를 객체처럼 다룹니다. 그러나 자바에서는 객체를 무조건 클래스 단위로 구현해야해서 이 함수도 결국 클래스와 유사하게 구현해야합니다.
따라서 자바의 함수 구현은 class로 구현해야하며 Function<T, R>를 상속받아야 합니다.
우선 파라미터를 받아서 10 더한 값을 반환하는 Add10이라는 함수를 만들어본다.
Function<>을 상속받아 Class Add10을 구현한 후, Abstract method인 apply를 구현한다.
import java.util.function.Function;
public class Add10 implements Function<Integer, Integer> {
@Override
public Integer apply(Integer integer) {
return integer + 10;
}
}
그 후, Main문에서 다음과 같이 선언한 후, apply 메소드를 통해 함수를 실행할 수 있다.
public class Main {
public static void main(String[] args){
Function<Integer, Integer> add10 = new Add10();
int result = add10.apply(5);
System.out.println(result);
}
}
위 경우는 파라미터가 1개인 경우다. 그렇다면 파라미터가 2개인 경우에는 어떻게 해야하는가?
그럴 때 사용하는 것이 BiFunction<T, U, R>이다.
사용법은 위 Function과 같으며 아래와 같이 구현할 수 있다.
public class BiAdd implements BiFunction<Integer, Integer, Integer> {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
}
public class Main {
public static void main(String[] args){
BiFunction<Integer, Integer, Integer> add = new BiAdd();
int result = add.apply(5, 10);
System.out.println(result);
}
}
자바에서 지원하는 Function으로는 파라미터 최대 2개까지 밖에 사용할 수 없다.
따라서 3개 이상을 사용하기 위해선 커스텀 Function Interface를 구현해야하는데 이때 사용할 수 있는 것이 바로 @FunctionalInterface다.
이 어노테이션을 통해 커스텀 Function Interface를 구현할 수 있으며 이를 상속받아 함수로 구현할 수 있다.
그 후에는 기존 Function과 BiFunction과 사용법은 같다.
@FunctionalInterface
public interface TriFunction<T, U, V, R> {
R apply(T t, U u, V v);
}
public class TriAdd implements TriFunction<Integer, Integer, Integer, Integer>{
@Override
public Integer apply(Integer integer, Integer integer2, Integer integer3) {
return integer + integer2 + integer3;
}
}
public class Main {
public static void main(String[] args){
TriFunction<Integer, Integer, Integer, Integer> triAdd = new TriAdd();
int result = triAdd.apply(1,2,3);
System.out.println(result);
}
}
위 방식으로 프로그래밍을 하면 새로운 함수가 필요할 때 마다 객체를 만들어줘야한다. 그러면 너무 많은 구현체가 생겨 유지보수나 디버깅이 어렵다.
이러한 문제를 해결하기 위해 Lambda 표현식을 사용할 수 있다. 람다는 익명함수를 뜻하며 런타임시 생성되어 바로 변수에 할당할 수 있도록 해준다.
사용방법은 아래와 같으며 상황에 따라 표현이 간결해질 수 있다.
// 기본 형태
(Object x) ->{
return x + 10
}
// 1. 타입이 추론 가능할 경우 x 타입 생략 가능
(x) -> {
return x + 10
}
// 2. 파라미터가 1개인 경우 () 생략 가능
x -> {
return x+10
}
// 3. 바로 return할 시, { return } 생략 가능
x -> x + 10
이 Lambda를 사용할 시, Add10, Add, TriAdd 객체 구현체가 필요없어지며 Main 코드는 아래와 같이 수정할 수 있다.
public class Main {
public static void main(String[] args){
// 파라미터 1개
Function<Integer, Integer> add10 = x -> x + 10;
int result1 = add10.apply(5);
System.out.println(result1);
// 파라미터 2개
BiFunction<Integer, Integer, Integer> add = (x, y) -> x+y;
int result2 = add.apply(5, 10);
System.out.println(result2);
// 파라미터 3개
TriFunction<Integer, Integer, Integer, Integer> triAdd = (x, y, z) -> x+y+z;
int result3 = triAdd.apply(1,2,3);
System.out.println(result3);
}
}