람다 표현식이란?
람다 표현식은 Java에서 익명 함수(anonymous function)
라고도 부르며 익명 함수를 나타내는 방법입니다. Java SE 8 릴리스의 일부로 Java 8
부터 도입되었고 함수를 짧고 간결한 코드 블록으로 정의한 다음 매개 변수로 전달하거나 변수에 할당할 수 있습니다. 이렇게 하면 코드를 더 쉽게 읽을 수 있고 유지 관리할 수 있을 뿐만 아니라 함수형 프로그래밍을 Java로 쉽게 작성할 수 있습니다.
메서드가 1번만 사용되고 메서드 길이가 짧은 경우에 매우 유용하다.
1
2
3
4
5
6
7
8
9
10
public class Lambda {
interface Adder {
int add(int x, int y);
}
public static void main(String[] args) {
Adder adder = (x, y) -> x + y;
System.out.println(adder.add(1, 2));
}
}
람다식은 익명의 객체이다
자바에서 함수(method)는 혼자서 생존할 수 없다. 객체가 아니기 때문에 클래스도 만들어야 하고, 객체도 생성해야 쓸 수 있다. 하지만 람다식으로 인해 변수처럼 스스로 존재하며 매개변수로 전달하거나 결과로 반환될 수 있다.즉, 람다식은 객체로 취급된다. 그렇다면 람다식을 다루기 위한 참조 변수 타입은 어떤 것으로 사용해야 하는가?
함수형 인터페이스
함수형 인터페이스(Functional Interface)는 단 하나의 추상 메서드만을 가지는 인터페이스를 말한다. 람다식은 함수형 인터페이스의 인스턴스를 생성하는 것이다. 따라서 람다식을 사용하기 위해서는 먼저 함수형 인터페이스를 정의해야 한다.
함수형 인터페이스의 조건
- 추상 메서드는
@FunctionalInterface
어노테이션을 사용하여 명시할 수 있다. - 추상 메서드가 2개 이상이면 컴파일 에러가 발생한다.
- 추상 메서드가 0개이면 컴파일 에러가 발생한다.
- 추상 메서드가 1개이면 람다식을 사용할 수 있다.
Example Code
자바에서는 기본적으로 제공되는 함수형 인터페이스가 몇 가지 있다.
java.util.function
패키지에Function
,Consumer
,Supplier
,Predicate
등이 있다.java.lang
패키지에는Runnable
인터페이스가 있다.
그 중 Consumer
인터페이스를 사용한 예제를 살펴보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> {
accept(t);
after.accept(t);
};
}
}
Consumer 인터페이스는 사용자로 부터 입력을 받아서 처리하는 역할을 하는 인터페이스이며 accept
메서드는 입력을 받아서 처리하는 역할을 한다. andThen
메서드는 Consumer
인터페이스를 리턴하는데, Consumer
인터페이스는 accept
메서드를 가지고 있기 때문에 accept
메서드를 연속적으로 호출할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class School extends AbstractService<Student> {
public void eatLaunch(String id) {
Student student = repo.findById(UUID.fromString(id))
.orElseThrow(() -> new AppException(AppError.FAIL_TO_QUERY_DB));
doSomethingIfHaveEatenLaunch(student, entity -> doSomething(entity));
}
}
public abstract class AbstractService<T> {
protected void doSomethingIfHaveEatenLaunch(T student, Consumer<T> consumer) {
Optional.ofNullable(student).ifPresent(entity -> {
if (!entity.isDone()) {
consumer.accept(entity);
entity.setDone(true);
repository.save(entity);
} else {
throw new AppException(AppError.ALREADY_DONE);
}
});
}
}