[Java] 자바 람다식과 함수형 인터페이스(Functional Interface) - (2)
kindof
·2021. 12. 6. 17:00
1. 들어가면서
지난 포스팅에서는 람다식의 기본적인 개념과 작성법, 그리고 함수형 인터페이스를 설명하면서 람다식이 어떻게 동작하는지에 대해 설명했습니다.
이번 시간에는 함수형 인터페이스와 관련된 이야기를 조금 더 해보려고 하는데요.
"람다식이 함수형 인터페이스를 기반으로 동작하는 것이라면, 람다식을 쓰기 위해서는 매번 함수형 인터페이스를 일일이 만들어야하는가?" 라는 물음이 자연스레 생기게 됩니다.
그래서 자바는 람다식을 편하게 이용할 수 있도록 유용한 함수형 인터페이스들을 몇 가지 미리 만들어두었습니다. 지난 시간에 살펴본 Comparator 인터페이스가 그런 예시일 수 있고, 이번 시간에 알아볼 내용들이 바로 그런 예시들입니다.
2. java.util.function 패키지
지난 시간에 설명했듯이, 람다식이 반환 타입과 매개 변수 타입 등을 지정하지 않아도 되는 이유는 함수형 인터페이스에서 정의한 메서드(1개)와 그 형태가 동일했기 때문입니다.
그런데 사실 대부분의 메서드는 매개변수가 없거나, 한 개 혹은 두개, 반환값은 없거나 한 개인 경우가 많습니다. 그리고 제네릭을 사용하면 매개변수의 타입이나 반환 타입 역시 신경쓰지 않아도 되죠.
그래서 java.util.function 패키지는 일반적으로 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 미리 정의해두었습니다. 그래서 우리는 매번 새로운 함수형 인터페이스를 정의하지 않고도 여기에 있는 함수형 인터페이스를 활용해서 람다식을 작성할 수 있는 것이죠.
한편, java.util.function 패키지 안에는 아래와 같이 정말 많은 함수형 인터페이스들이 존재하는데요.
이 많은 인터페이스들을 하나하나 다 소개하기에는 제가 다 소화를 하지 못할 것 같아서 자주 사용하고 대표적인 Function과 Predicate에 대해서 우선 정리해보겠습니다.
2-1. Function
Function이라는 함수형 인터페이스는 아래와 같이 apply() 추상 메서드를 가지고 있으며, compose()나 andThen() 같은 default 메서드도 가지고 있습니다. Function은 함수형 인터페이스는 하나의 매개변수(T)를 입력받아서 결과(R)를 리턴해줍니다.
예제를 보겠습니다.
convertListToMap 메서드는 Function 자체를 매개변수로 받고 있습니다. 그리고 우리가 원하는 로직을 매개변수 형태로 넘겨주게 되면, convertListMap 메서드 안에서 해당 로직을 apply()하여 결과를 리턴해주는 것입니다.
예를 들어, 위 코드에서는 리스트를 넘겨주고 그 안의 원소들을 Key값으로 하고 원소의 길이를 Value값으로 하는 Map을 만들어 리턴해주고 있죠.
2-2. Predictate
Predicate은 특정값을 받아서 boolean 값을 반환하는 람다식을 작성할 때 사용하는 함수형 인터페이스로, 코드 내부를 들여다보면 아래와 같은 메서드가 존재합니다.
Predicate는 test()라는 하나의 추상 메서드를 가지고 있으며 이전 포스팅에서도 잠깐 언급했듯이, default 메서드를 여러 개 가지고 있습니다.
각 메서드의 설명을 읽어보시면, test()는 인자를 전달받아서 boolean 값을 리턴하고, and()는 Predicate들의 결과를 AND 연산하여 그 결과를 리턴합니다.
또한, negate()는 Predicate가 리턴하는 값과 반대되는 값을 리턴하는 Predicate를 리턴하고, or()는 and()처럼 Predicate들을 OR 연산하여 그 결과를 리턴합니다.
예제를 보겠습니다. 한 학생의 시험 성적을 입력받아서 그 결과가 평균보다 높은지를 반환해주는 Predicate입니다.
지금은 그냥 학생 한 명에 대해 써보는 간단한 예제이지만, Predicate을 적절히 응용해서 사용하면 더 길어지는 코드가 있을 때나 실제 비즈니스 로직에서 메서드를 담당하는 클래스 등을 따로 설계할 필요없이 쉽게 짤 수 있겠죠?
그리고 위에서 소개한 default 메서드로 여러 조건들을 체이닝(Chaining)해서 결과값을 리턴할 수도 있겠습니다.
아래 예제는 리스트의 원소 중에서 5 이상의 값만 추출해내고 싶을 때 filter()와 Predicate를 같이 쓰고 있는데요.
실제로 Stream 인터페이스 안에 있는 filter() 메서드는 Predicate를 매개변수로 받고 있는 것을 확인하실 수 있습니다.
3. 나가면서
이번 포스팅에서는 자바에서 제공하는 함수형 인터페이스 Function과 Predicate에 대해 살펴봤습니다. 위에서 예시로 본 것처럼 많은 경우 람다식을 매개변수로 넘겨서 코드를 간결하게 짤 수 있는 것 같습니다.
개인 프로젝트를 진행하면서도 위와 같은 람다식을 적용해서 개선할 수 있는 부분들을 리팩토링 해봐야겠습니다!
감사합니다.
'Java & Kotlin' 카테고리의 다른 글
[Java] Thread 안에서 발생하는 예외는 어떻게 처리되나 (0) | 2023.03.31 |
---|---|
[JAVA] JDK 17에서 제공하는 새로운 기능들 정리해보기 (0) | 2022.11.18 |
[Java] LinkedList, ArrayList를 구현하는 방식과 이에 따른 성능 비교해보기 (0) | 2021.12.02 |
[Java] 제네릭(Generics)에 대해 생각해보기 (0) | 2021.11.23 |
[JAVA] 자바 hashCode() (0) | 2021.11.20 |