javaScript를 공부하다보면 람다 표현식이라는 것을 배우게 된다.
javaScript에서는 람다 함수를 화살표 함수라고 부른다.
// 일반 함수
function printHello(){
console.log('Hello');
}
// 화살표 함수
const printHello = () => console.log('Hello');
그런데 Java에도 람다가 있다는 것을 알게 되었다.
형태는 JS와 매우 비슷하다.
IntConsumer printInt = (i) -> {
System.out.println(i + baseNumber);
};
똑같이 화살표를 쓴다.
바디는 화살표의 오른똑에 함수 본문을 정의하고, 여러 줄인 경우에 {}를 사용해서 묶는다. 또한 한 줄이라면 생략도 가능하고, return 도 생략 가능하다.
람다의 큰 특징 중 하나는 변수 캡쳐이다.
람다의 바디에서 지역 변수를 사용하게 되면 해당 지역 변수가 사용된다.
람다를 감싸고 있는 영역의 지역 변수
package Foo;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
public class Foo {
public static void main(String[] args) {
Foo foo = new Foo();
foo.run();
}
private void run() {
int baseNumber= 10;
IntConsumer printInt = (i) -> {
System.out.println(i + baseNumber);
};
printInt.accept(2);
}
}
이 경우 baseNumber는 람다를 감싸고 있는 run() 의 지역변수인 baseNumber이다. 단 이 때, baseNumber가 final이 아니라면 불가하다.
그럼 위 경우에는 final이라고 선언하지 않았는데 왜 가능할까?
선언하지 않았어도 effective final(사실상의 final , 변수 선언 후 수정 안 하는 경우)라면 가능하다.
람다와 익명 클래스, 내부 클래스 모두 final 지역 변수를 참조 할 수 있다.
package Foo;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
public class Foo {
public static void main(String[] args) {
Foo foo = new Foo();
foo.run();
}
private void run() {
int baseNumber= 10;
class LocalClass {
void printBaseNumber() {
System.out.println(baseNumber);
}
};
Consumer<Integer> integerConsumer = new Consumer<Integer> () {
public void accept(Integer integer) {
System.out.println(baseNumber);
}
};
IntConsumer printInt = (i) -> {
System.out.println(i + baseNumber);
};
LocalClass lc = new LocalClass();
lc.printBaseNumber();
printInt.accept(2);
integerConsumer.accept(2);
}
}
이 경우 모두 baseNumber를 참조할 수 있다.
그렇지만 매우 큰 차이가 있다.
익명클래스, 내부클래스 와 람다의 차이점은 바로 Shadowing
이다.
람다는 익명 클래스 구현체와 달리 ‘쉐도윙’하지 않는다.
익명 클래스는 새로 스콥을 만들지만, 람다는 람다를 감싸고 있는 스콥과 같다.
익명 클래스와 로컬 클래스는 또 다른 스콥이므로 쉐도잉이 일어나지만, 람다는 감싸고 있는 것(여기서는 run)과 같은 스콥을 갖는다.
package Foo;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
public class Foo {
public static void main(String[] args) {
Foo foo = new Foo();
foo.run();
}
private void run() {
int baseNumber= 10;
class LocalClass {
void printBaseNumber() {
int baseNumber = 11;
System.out.println(baseNumber); // 11
}
};
Consumer<Integer> integerConsumer = new Consumer<Integer> () {
public void accept(Integer baseNumber) {
System.out.println(baseNumber); // 2
}
};
IntConsumer printInt = (baseNumber) -> {
System.out.println(i + baseNumber); // 안됨!!
};
LocalClass lc = new LocalClass();
lc.printBaseNumber();
integerConsumer.accept(2);
printInt.accept(2);
}
}
위의 경우를 보면 내부 클래스와 익명 클래스는 각자의 scope을 가지므로 baseNumber를 지역 변수로 선언할 수 있다.
그렇지만 람다의 경우 run() 과 같은 scope를 가지므로 같은 변수를 또 선언할 수 없어서 에러가 발생한다.