Lambda
람다식(lambda expression)은 이름 없는 함수를 식 하나로 표현하는 문법이다. Java에서는 메서드가 하나뿐인 인터페이스의 구현을 짧게 쓰는 방법으로 동작한다. Java 튜토리얼은 람다식이 기능을 메서드 인자로, 코드를 데이터처럼 다룰 수 있게 해 준다고 설명한다 (Oracle).
왜 필요한가
람다식 이전에는 동작을 인자로 넘기려면 익명 클래스를 써야 했다. 정렬 기준이나 필터 조건처럼 한 줄짜리 로직도 클래스 선언, 메서드 시그니처, 중괄호를 모두 적어야 했다. 람다식은 컴파일러가 알 수 있는 부분을 생략해서 동작 자체만 남긴다.
1
2
3
4
5
6
7
8
9
10
// 익명 클래스
Comparator<String> byLength = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return Integer.compare(a.length(), b.length());
}
};
// 람다식
Comparator<String> byLength2 = (a, b) -> Integer.compare(a.length(), b.length());
문법
람다식은 괄호 안의 매개변수 목록, 화살표 ->, 본문으로 이루어진다.
- 매개변수 타입은 생략할 수 있다.
- 매개변수가 하나면 괄호도 생략할 수 있다.
p -> p.getAge() >= 18 - 본문이 식 하나면 그 값이 반환된다. 문장 여러 개라면 중괄호로 감싸고
return을 쓴다.
1
2
3
4
5
6
7
Predicate<Integer> isAdult = age -> age >= 18;
Function<String, Integer> length = s -> s.length();
Consumer<String> printer = s -> System.out.println(s);
BinaryOperator<Integer> add = (a, b) -> {
int sum = a + b;
return sum;
};
함수형 인터페이스와 타깃 타입
람다식은 혼자서는 타입이 없다. 컴파일러는 람다식이 놓인 위치(변수 선언, 대입, 반환문, 메서드 인자 등)가 기대하는 타입, 곧 타깃 타입을 보고 어떤 인터페이스의 구현인지 정한다. 이때 타깃이 될 수 있는 것이 함수형 인터페이스다. 튜토리얼의 정의로는 추상 메서드가 정확히 하나인 인터페이스이고, default 메서드나 static 메서드는 몇 개가 있어도 된다. 추상 메서드가 하나뿐이므로 람다식에서 메서드 이름을 생략할 수 있다.
java.util.function 패키지는 자주 쓰는 형태를 미리 정의해 둔다.
| 인터페이스 | 메서드 | 용도 |
|---|---|---|
Predicate<T> | boolean test(T t) | 조건 검사, 필터 |
Consumer<T> | void accept(T t) | 값을 받아 동작 수행 |
Function<T,R> | R apply(T t) | 값을 다른 값으로 변환 |
본문이 기존 메서드 호출 하나뿐이라면 String::length, System.out::println 같은 메서드 참조로 더 줄일 수 있다.
주의할 점
- 람다식은 바깥 블록의 지역 변수와 매개변수 중 final이거나 사실상 final(effectively final)인 것만 읽을 수 있다. 초기화 이후 값을 바꾸는 변수를 참조하면 컴파일 오류가 난다.
1
2
int count = 0;
list.forEach(s -> count++); // 컴파일 오류: effectively final이 아니다
- 람다식은 렉시컬 스코프를 따른다. 새로운 스코프를 만들지 않으므로 바깥 지역 변수와 같은 이름의 매개변수를 선언할 수 없고, 본문의
this는 람다식을 감싼 객체를 가리킨다. 익명 클래스의this가 익명 클래스 자신을 가리키는 것과 다르다. - 람다식은 짧을 때 읽기 쉽다. 본문이 길어지면 이름 있는 메서드로 빼고 메서드 참조로 넘기는 편이 낫다.
참고한 자료외부 출처 2 · 블로그 글 2
이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.
댓글
아직 댓글이 없습니다