[Java-14] 람다식(Lambda expression)
1.람다식(Lambda expression)
1) 람다식이란?
- 메서드를 하나의 '식(expression)'으로 표현한 것
- 함수를 간략하면서도 명확한 식으로 표현할 수 있게 해줌
- JDK1.8부터 추가
- 익명 함수(anonymous function)라고도 함
2) 람다식 작성
반환타입 메서드이름(매개변수 선언) {
문장들
}
↓반환타입 메서드이름(매개변수 선언) -> {
문장들
}
- 기존 메서드
int max(int a, int b) {
return a > b ? a : b;
}
- 이름과 반환타입 제거
(int a, int b) -> {
return a > b ? a : b;
}
- return문 대신 '식'으로 표현 가능
- 연산결과는 자동적으로 반환값이 됨
- 끝에 ';'을 붙이지 않음
(int a, int b) -> a > b ? a : b
- 추론이 가능한 경우 매개변수의 타입 생략 가능
- 매개변수 중 하나의 타입만 생략하는 것은 불가능
(a, b) -> a > b ? a : b
- 선언된 매개변수가 하나뿐일 경우 괄호() 생략 가능 (단, 매개변수의 타입이 없어야 함)
a -> a * a // 가능
int a -> a * a // 불가능
- 괄호{} 안의 문장이 하나일 때는 괄호{} 생략 가능 (단, return문이 아니어야 함)
- 문장의 끝에 ';'를 붙이지 않음
(String name, int i) -> {
System.out.println(name+"="+i);
}
(String name, int i) -> System.out.println(name+"="+i)
3) 함수형 인터페이스(Functional Interface)
- 람다식을 다루기 위한 인터페이스
- 인터페이스를 구현한 익명 객체를 람다식으로 대체할 수 있기 때문
(익명 개체의 메서드와 람다식의 매개변수의 타입, 개수, 반환값이 일치할 경우)
- 함수형 인터페이스에는 하나의 추상 메서드만 정의되어 있어야 함
(람다식과 인터페이스의 1:1 연결을 위해)
- default와 static 메서드의 개수에는 제약이 없음
@FunctionalInterface
interface MyFunction {
public abstract int max(int a, int b);
}
※ 메서드의 매개변수가 함수형 인터페이스 타입일 때
- 해당 메서드를 호출할 때 람다식을 참조하는 참조변수를 매개변수로 지정
- 또는 직접 람다식을 매개변수로 지정
@FunctionalInterface
interface MyFunction {
void myMethod();
}
void aMethod(MyFunction f) {
f.myMethod();
}
...
// (1) 람다식을 참조하는 참조변수를 매개변수로
MyFunction f = ()-> System.out.println("myMethod()");
aMethod(f);
// (2) 직접 람다식을 매개변수로
aMethod(()-> System.out.println("myMethod()"));
※ 메서드의 리턴타입이 함수형 인터페이스 타입일 때
- 함수형 인터페이스와 동등한 람다식을 참조하는 참조변수를 반환
- 또는 직접 람다식을 반환
@FunctionalInterface
interface MyFunction {
void myMethod();
}
MyFunction myMethod() {
// (1) 함수형인터페이스와 동등한 람다식을 가리키는 참조변수 반환
MyFunction f = ()->{};
return f;
// (2) 직접 람다식을 반환
return ()->{};
}
※ 람다식의 타입과 형변환
- 람다식의 타입이 함수형 인터페이스의 타입과 일치하는 것은 X
- 형변환이 필요하지만 생략 가능함
- 람다식은 오직 '함수형 인터페이스'로만 형변환 가능
MyFunction f = (MyFunction) (()->{});
Object obj = (Object) (()->{}); // Error
Object obj = (Object)(MyFunction)(()->{});
String str = ((Object)(MyFunction)(()->{})).toString();
※ 외부 변수를 참조하는 람다식
- 람다식 내에서 참조하는 지역변수는 final이 붙지 않았어도 '상수'로 간주되므로 변경 불가능
@FunctionalInterface
interface MyFunction2 {
void myMethod(); // public absract void myMethod();
}
class Outer {
int val = 10;
class Inner {
int val = 20;
void method(int i) {
int val = 30;
MyFunction2 f = () -> {
System.out.println("i: " + i); // i는 상수로 취급하여 변경 불가능, 100
System.out.println("val: " + val); // val은 상수로 취급하여 변경 불가능, 30
System.out.println("this.val: " + ++this.val); // Inner 클래스의 val이므로 변경 가능, 21
System.out.println("Outer.this.val: " + ++Outer.this.val); // Outer 클래스의 val이므로 변경 가능, 11
};
f.myMethod();
}
}
}
public class LambdaEx2 {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.method(100);
}
}
4) java.util.function패키지
- 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 미리 정의해놓은 것
① 주요 함수형 인터페이스
java.lang.Runnable | 매개변수 X, 반환값 X |
Supplier<T> | 매개변수 X, 반환값 O |
Consumer<T> | 매개변수 O, 반환값 X |
Function<T, R> | 매개변수 1, 반환값 1 |
Predicate<T> | 조건식 표현 매개변수 1, 반환값 boolean |
Predicate<String> isEmptyStr = s -> s.length() == 0;
String s = "";
if(isEmptyStr.test(s))
System.out.println("This is an empty String.");
② 매개변수가 2개인 함수형 인터페이스
BiConsumer<T, U> | 매개변수 2, 반환값 X |
BiPredicate<T, U> | 조건식 표현 매개변수 2, 반환값 boolean |
BiFunction<T, U, R> | 매개변수 2, 결과 1 |
5) 메서드 참조(method reference)
- 람다식이 하나의 메서드만 호출하는 경우, 람다식을 간략하게 하는 것
종류 | 람다 | 메서드 참조 |
static메서드 참조 | (x) -> ClassName.method(x) | ClassName::method |
인스턴스메서드 참조 | (obj, x) -> obj.method(x) | ClassName::method |
특정 객체 인스턴스메서드 참조 | (x) -> obj.method(x) | obj::method |
참고 - Java의 정석 3rd Edition (저자 : 남궁성, 출판 : 도우출판)