Notice
suyeonme
[Java] 람다 표현식(Lamda Expression)과 함수형 인터페이스(Functional Interface) 본문
프로그래밍👩🏻💻/Java
[Java] 람다 표현식(Lamda Expression)과 함수형 인터페이스(Functional Interface)
suyeonme 2023. 3. 1. 15:57모던 자바 인 액션(Modern Java in Action)을 읽고 정리한 내용입니다.
람다 표현식(Lamda Expression)이란?
람다식은 자바에서 제공하는 함수형 프로그래밍 방식이다. 람다는 익명 함수(anonymous function)로, 함수를 일급값으로 넘기는 프로그램을 구현하는 것이다. 함수형 인터페이스를 구현하여 람다식을 사용한다.
람다식은 아래와 같은 특징이 있다.
- 변수에 할당할 수 있다.
- 함수형 인터페이스를 인수로 받는 메서드로 전달할 수 있다.
- 함수형 인터페이스의 추상 메서드와 같은 시그니처를 갖는다.
람다식 문법
() -> expression // expression style
() -> { statements; } // block style
() -> "Suyeon"
() -> { return "Suyeon"; }
(x,y) -> x >= y ? x : y;.
람다식에서 지역 변수 사용
- 람다 표현식에서는 익명 함수와 마찬가지로 자유 변수(free variable)를 사용할 수 있다. 이와 같은 동작을 람다 캡처링(capturing lamda)라고 한다. 이 때, 자유 변수란 파라미터로 넘겨진 변수가 아닌 외부에서 정의된 변수를 의미한다.
- 지역 변수를 자유롭게 캡쳐할 수 있지만, 지역 변수는 final로 선언되거나, 실질적으로 final로 선언된 변수와 똑같이 사용되어야한다. 람다식안에서 사용되는 지역변수는 final을 사용하지 않아도 상수로 간주된다.
람다식 활용하기
사용 사례 | 예시 |
불리언 표현 | (List<String>) → list.isEmpty() |
객체 생성 | () → new Apple(10) |
객체에서 소비 | (Apple a) → System.out.println(a.getWeight) |
객체에서 선택/추출 | (String s) → s.length() |
두 값 조합 | (int a, int b) → a * b |
두 객체 비교 | (Apple a1, Apple a2) → a1.getWeight().compareTo(a2.getWeight()) |
람다식 형식 추론
- 자바 컴파일러는 람다 표현식이 사용된 컨텍스트를 이용해서 람다 표현식과 관련된 함수형 인터페이스를 추론한다. 대상 형식을 이용해서 함수 디스크립터를 알 수 있으므로 컴파일러는 람다의 시그니처도 추론할 수 있다.
- 따라서 컴파일러는 람다 표현식의 파라미터 형식에 접근할 수 있으므로 람다문법에서 이를 생략할 수 있다.
Comparator<Apple> c = (Apple a1, Apple a2) -> a1.getWeight().comareTo(a2.getWeight());
Comparator<Apple> c = (a1, a2) -> a1.getWeight().comareTo(a2.getWeight());
람다 표현식 사용 예시
아래는 동작 파라미터화(전략 패턴) ⇒ 익명 클래스 ⇒ 람다 표현식 ⇒ 메서드 참조 형태로 점차 개선해나가는 코드이다.
1) 동작 파라미터화(전략 패턴)
comparator를 사용하여 구현한다.
public class AppleComparator implements Comparator<Apple> {
public int compare(Apple a1, Apple a2) {
return a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new AppleComparator());
2) 익명 클래스 사용
inventory.sort(new Comparator<Apple>() {
public int compare(Apple a1, Apple a2) {
return a1.getWeight().compareTo(a2.getWeight());
}
});
3) 람다 표현식 사용
// 3-1. 람다 표현식 사용
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
// 3-2. comparing 메서드와 람다 표현식 사용
inventory.sort(comparing(apple -> apple.getWeight()));
4) 메서드 참조 사용
코드가 매우 간략해졌으며 가독성도 좋아졌다!
inventory.sort(comparing(Apple::getWeight));
함수형 인터페이스
함수형 인터페이스란, 하나의 추상 메서드를 정의하는 인터페이스로 @FunctionalInterface로 표기한다. 함수형 인터페이스의 추상 메서드 시그니처를 함수 디스크립터라고 한다.
람다식을 구현하려면 함수형 인터페이스를 만들고 인터페이스에 람다식으로 구현할 메서드를 선언한다.
- 추상 메서드의 시그니처(함수 디스크립터)는 람다 표현식의 시그니처를 정의한다.
- 함수형 인터페이스의 추상 메서드는 람다 표현식의 시그니처를 묘사한다.
* 람다식 사용시, 함수형 인터페이스를 구현해야하는 이유
람다식은 익명 함수로 구현하기 때문에 인터페이스에 여러개의 메서드가 있다면 어떤 메서드를 구현해야하는지 모호하다.
함수형 인터페이스의 경우 오직 하나의 추상 메서드를 지정하기때문에 위와같은 문제를 해결할 수 있다.
함수형 인터페이스의 종류
대표적으로 많이 사용하는 함수형 인터페이스 목록이다.
함수형 인터페이스 | 함수 디스크립터 | 설명 |
Predicate<T> | T → boolean | 추상 메서드 test를 정의한다. (ex. filter) |
Consumer<T> | T → void | 추상 메서드 accept를 정의한다. (ex. forEach) |
Function<T,R> | T → R | 추상 메서드 apply를 정의한다. (ex. map) |
Supplier<T> | () → T |
Predicate 사용 예시 (negate, and, or)
Predicate<Apple> notRedApple = redApple.negate();
Predicate<Apple> readAndHeavyAppleOrGreen =
redApple.and(apple -> apple.getWeight() > 150)
.or(apple -> GREEN.equals(apple.getColor()));
함수형 인터페이스와 람다식 사용 예시
public interface Predicate<T> {
boolean test(T t);
}
static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p) {
List<Apple> result = new ArrayList<>();
for(Apple apple : inventory) {
if(p.test(apple)) {
result.add(apple);
}
}
return result;
}
// 함수 사용
filterApples(inventory, (Apple apple) -> GREE.equals(apple.getColor());
filterApples(inventory, (Apple apple) -> apple.getWeight() > 150);
'프로그래밍👩🏻💻 > Java' 카테고리의 다른 글
[Java] Optional 클래스 (0) | 2023.04.01 |
---|---|
[Java] 메서드 참조(Method Reference)란? (0) | 2023.03.01 |
[Java] Boxing, Unboxing이란? (0) | 2023.03.01 |
[Java] Servlet이란? (1) | 2022.10.11 |
[Java] BufferedReader/BufferedWriter (0) | 2022.08.07 |
Comments