람다식

고재석·2021년 6월 22일
0

Java는 기본!

목록 보기
9/12

람다식과 함수형 프로그래밍

함수평 프로그래밍은 프로그래밍 패러다임의 하나로 자료를 특정 상태를 바꾸는 것이 아닌 함수를 구현하여 실행하고 결과를 얻는 방식으로 처리하는 프로그래밍 기법이다. 함수형 프로그래밍은 병렬 처리와 이벤트 지향 프로그래밍에 적합하기에 자바 8부터 지원하기 시작했다.

함수형 프로그래밍을 지원하는 대표적인 기능이 람다식이다. 람다식은 익명 함수를 생성하기 위한 기능으로 자바 코드가 매우 간결해지고, 컬렉션의 요소를 쉽게 집계할 수 있도록 한다.


람다식 기본 사용 문법

그렇다면 기본적인 람다식 사용 문법을 알아보자.

기본적으로 람다식은 (매개변수) -> {실행문}의 문법을 가진다. 아래의 코드를 보자.

@FunctionalInterface
public interface MyFunctionalInterface {
    public void method(int x, int y);
}


MyFunctionalInterface mfi = (x, y) -> { return x + y; };
int result = mfi.method(1, 2); // 3

MyFunctionalInterface라는 인터페이스를 만들고 추상 메소드를 하나 만들었다. 그리고 이 인터페이스를 FunctionalInterface Annotation으로 함수형 인터페이스로 선언했다. (추상 메소드가 하나인 경우는 따로 명시할 필요가 없긴 하다.) 이 인터페이스의 익명 구현 객체를 만들어서 사용하고자 한다. method() 메소드를 오버라이딩해서 구현해도 되지만 코드가 많이 복잡해진다. 이 경우에는 method() 메소드에서 받고자 하는 파라미터로 어떤 자료 처리를 할 것인지 람다식으로 표현하면 위와 같이 깔끔하게 구현할 수 있다.


함수적 인터페이스 API

자바에서 한 개의 추상 메소드를 가지는 인터페이스들은 모두 람다식으로 익명 객체를 구현할 수 있다. 그런데 타입이나 파라미터의 갯수 등 여러 메소드를 함수형 프로그래밍으로 구현하기 위해 각각 인터페이스 코드를 작성해줘야 한다. 자바 8 이후로는 함수형 인터페이스 API를 제공함으로써 이러한 번거로움을 해결하기 위해 지원한다. java.util.function 표준 API 패키지로 지원하고 있으며 그 종류는 크게 Consumer, Supplier, Function, Operator, Predicate로 구분된다.

Consumer로 이해하기

Consumer는 매개값은 있고 리턴값은 없는 함수형 인터페이스이다. 파라미터의 타입과 수에 따라서 여러 Consumer들이 있다.

아래의 코드를 보고 Consumer를 이해해보자.

람다식으로 구현할 객체의 로직(함수)는 매개 변수를 받아서 작업을 하지만 리턴값은 존재하지 않는다. 따라서 Consumer를 사용하는 것이고, 매개변수의 타입마다 각기 다른 Consumer를 사용해서 구현한다.


함수형 인터페이스의 디폴트 메소드

지금까지는 하나의 함수형 인터페이스로 로직을 구성하는 방법을 알아봤다. 하지만 andThen()과 compose()메소드를 활용하면 두 개 이상의 함수형 인터페이스로 하나의 결과 값이나 매개변수를 다시 매개 변수로 넘겨주는 기능을 사용할 수 있다. 아래의 코드를 참고해보자.

andThen()과 compose() 메소드는 순서가 바뀌는 것이라고 생각하면 된다. andThen()메소드를 활용한 첫번째 람다식의 경우에는 functionA가 먼저 실행되고 Address 인스턴스가 반환되어 functionB의 매개변수로 전달된다. 그래서 최종적으로 functionB가 실행되어 city 필드의 값을 반환하게 된다.


Predict 종류의 함수적 인터페이스는 and(), or(), negate() 디폴트 메소드를 가지고 있다. 이 메소드들은 andThen()과 compose()와 같이 두 개 이상의 함수형 인터페이스를 활용하는 기능을 제공한다. 각각 &&, ||, ! 연산자와 대응된다고 생각하면 된다.

위의 코드와 같이 두 개의 논리값을 연산하여 최종 논리값을 반환하게 된다.


메소드, 생성자 참조

메소드 참조, 생성자 참조는 말 그대로 특정 클래스의 메소드나 생성자만을 참조하여 불필요한 매개 변수를 제거하는 기능이다. 아래의 코드와 같이 메소드를 참조할 수 있다.

IntBinaryOperator operator = Math::max;

이렇게 선언하면 operator로 선언된 익명 객체는 두 매개 변수를 입력받아 최대값을 반환하는 로직을 가진 함수로 구현된다. 이렇게 메서드나 생성자를 참조하게 되면 기존의 코드를 재사용해서 함수형 프로그래밍을 활용할 수 있다는 장점이 있고, 코드도 훨신 깔끔해진다. 생성자를 참조하는 코드는 아래와 같다.

// new Member(String id)
Function<String, Member> function = Member::new;
Member member = function.apply("jae-seok");
profile
명확하게 말하고, 꼼꼼하게 개발하자

0개의 댓글