[Java] 함수형 프로그래밍(Functional Programming, FP) 알아보기
함수형 프로그래밍이란
함수형 프로그래밍은 자료 처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임의 하나이다. - 위키페디아
보통은 프로그래밍 언어에서 Java, Python과 같은 객체 지향 언어와 C언어와 같은 절차 지향 언어가 친숙하겠지만 수학 함수처럼 코드를 작성할 수 있는 함수형 프로그래밍 언어도 존재합니다. 함수형 프로그래밍 언어를 사용하면 순수한 함수들을 조합하여 프로그램을 만들 수 있습니다. 가령 클로저, 스칼라 등이 있습니다.
함수형 프로그래밍은 이해하기 쉽다.
그리고 객체 지향 언어였던 Java도 버전 8부터 유연성을 위해 함수형 프로그래밍 개념을 도입하기 시작했습니다. 객체 지향은 개발자가 더 원활히 유지 보수를 할 수 있도록 도와주지만 대신 더 복잡하고 예상치 못한 코드를 양산하기 때문입니다. 예시로 스마트폰 기기에서 버튼을 눌렀을 때 텍스트뷰의 내용이 변하는 간단한 자바 프로그램을 살펴보도록 하겠습니다.
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
text.setText(edit.getText());
}
});
텍스트뷰의 내용을 바꾼다는 행위를 View.OnClickListener라는 클래스의 익명 객체를 만들어 내부 onClick() 메서드에서 구현하고 있습니다. 텍스트뷰의 내용을 바꾸는 코드는 text.setText(edit.getText()); 라는 단 한 줄인데도 불구하고 위의 코드는 Java의 객체 지향 특성으로 인해 행위 클래스의 객체를 만들고 메서드를 구현하는 방식으로 길고 복잡하게 작성되었습니다.
이렇듯 객체 지향 기법이 가져오는 가독성의 단점을 보완하기 위해 Java 8 버전부터는 람다, 스트림 API 등이 도입되면서 함수형 프로그래밍 기법을 일부 사용할 수 있게 되었습니다. Java 8의 람다를 사용해 아래의 익명 클래스를 사용한 코드를 한 번 간단화해보도록 하겠습니다.
익명 클래스를 사용한 예시
Collections.sort(inventory, new Comparator<Apple>() {
public int compare(Apple a1, Apple a2) {
return a1.getWeight().compareTo(a2.getWeight());
}
});
자바 8로 위 코드를 보완한 예시
inventory.sort(comparing(Apple::getWeight));
위의 Comparator처럼 Java의 람다를 사용해 마치 수학 함수처럼 표현할 수 있는 인터페이스들을 함수형 인터페이스라고 부릅니다. 함수형 인터페이스는 추상 메서드를 단 한 개만 가지고 있는 자바의 인터페이스를 의미합니다. 함수형 프로그래밍에서는 함수를 일급 객체로 취급하기 때문에, 함수 또한 상수나 변수처럼 파라미터로 전달할 수 있습니다. 아래는 자바에서 기본적으로 제공하는 Predicate 함수형 인터페이스의 코드입니다.
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
함수형 프로그래밍은 불변이다.
Java에 함수형 프로그래밍 기법이 도입된 것은 가독성만을 위한 것이 아닙니다. 2000년 초반대까지만 해도 단일 CPU로 동작되던 프로그램들이 확장되면서 단일 CPU 코어로는 성능에 한계가 느껴지기 시작했습니다. 이에 따라 점차 프로그램들이 코어 수를 늘려 CPU 성능을 향상시켰고 자연스레 프로그램에 있어 병렬성이 중요시되기 시작했습니다. Java는 병렬성을 중시하는 시대의 흐름을 따라 병렬 스레드에서도 안전성을 보장할 수 있는 불변한 함수형 프로그래밍 기법을 Java 8에 도입합니다.
함수형 인터페이스는 하나의 순수 함수로만 구성되어 있습니다. 불변인 이 순수 함수는 내부에 값을 변경하는 코드가 없으며 입력 파라미터에만 의존하기에 부수 효과를 일으키지 않습니다. 즉, 언제 어디서든 이 순수 함수는 같은 결과를 창출합니다. 마치 두 파라미터 값을 더해 리턴하는 add(2,3) 함수를 실행하면 반드시 5라는 결과가 나오는 것처럼 말이죠!
함수형 프로그래밍은 Null에 안전하다.
Null Pointer의 창시자 토니 호어는 Null에 대해 '10억 불짜리 실수'라고 발언한 적이 있습니다. 그만큼 많은 개발자들이 Null을 안전하게 처리하기 위해 많은 비용을 쏟고 있습니다. Java 8에서 도입된 Optional은 함수형 프로그래밍에서 고안해낸 개념으로 담긴 값이 Nullable하다는 것을 알려줘 미연에 NullPointerException을 방지합니다.
Optional<Integer> optional = Optional.ofNullable(getAnswer());
int answer = optional.orElse(-1); // 값이 Null이라면 -1 리턴
참고자료:
https://code-lab1.tistory.com/245
https://tecoble.techcourse.co.kr/post/2021-09-30-java8-functional-programming/