람다 표현식은 컴파일러의 추론에 의지해서 코드를 단순화하는 것
(인자 리스트) -> {바디}
Supplier<Integer> getNumber = () -> 20;
UnaryOperator<Integer> plus20 = (i) -> i + 20;
BinaryOperator<Integer> getSum = (x, y) -> x + y;
BinaryOperator<Integer> getSum = (Integer x, Integer y) -> x + y;
인자 리스트의 타입을 컴파일러가 추론하기 때문에 생략 가능
public class Dummy {
public static void main(String[] args) {
Dummy dummy = new Dummy();
dummy.run();
}
private void run() {
// 지역 변수
int baseNumber = 20;
// 람다 표현식 외부의 값, baseNumber 참조
IntConsumer printInt = (i) -> System.out.println(i+baseNumber);
printInt.accept(15);
}
}
지역변수를 참조하고 있을 때, 변수 캡처가 일어난다.
지역 변수는 JVM의 메모리 중 Stack 영역에 저장
각 쓰레드는 별도의 Stack을 가지고 있다
서로 다른 쓰레드 간 Stack 영역이 공유되지 않는다
만약, 람다의 쓰레드가 종료되기 전 참조하고 있는 지역변수의 쓰레드가 먼저 종료되어,
지역변수가 Stack에서 사라진다면 이는 오류로 이어질 수 있다
따라서 변수 캡처(Variable capture)를 통해 자신의 스택에 참조하고 있는 변수를 복사해두어 오류를 방지한다
값을 복사해오기 때문에 참조되는 변수는 변경되면 안 된다
Java8 이후로는 참조 변수가 final처럼 동작한다면 생략 가능하도록 변경
// ex)
private void run() {
int baseNumber = 20;
// 내부 클래스
class LocalClass {
void printBaseNumber() {
System.out.println(baseNumber);
}
}
// 익명 클래스
Consumer<Integer> integerConsumer = new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(baseNumber);
}
};
// 람다 표현식
IntConsumer printInt = (i) -> System.out.println(i + baseNumber);
printInt.accept(20);
}
내부 클래스와 익명 클래스는 '쉐도잉'한다.
람다의 스코프는 외부 변수와 같기 때문에 변수를 가질 수 없다.