람다 표현식은 메서드로 전달할수 있는 익명함수를 단순화 한것
특징
- 익명 - 이름이 없으므로 익명이라고 표현
- 함수 - 메서드 처럼 특정 클래스에 종속되지 않음.
- 전달 - 람다를 메서드로 전달하거나 변수로 저장할수 있음
- 간결성 - 익명클래스 처럼 많은 코드를 구현할 필요가 없음
한마디로 익명 클래스와 메서드의 단점을 버리고 장점만 조합해서 만든것이 람다이다.
람다는 람다 파라미터, 화살표, 람다 바디로 이루어져 있는데 (파라미터 ) -> 표현식; 이런식으로 구성된다.
람다를 언제 사용하는건가?
-> 함수형 인터페이스라는 문맥에서 람다 표현식을 사용 할수 있다.
함수형 인터페이스라는것을 알아야 하는데 자바 8의 특징중 하나가
[자바 8설계자는 이급시민을 일급 시민으로 바꾸는 기능을 추가 했다.
이급 시민이었던 함수를 일급값으로 넘겨주는 프로그래밍을 함수형 프로그래밍이라고 한다. ]
함수형 인터페이스는 정확히 하나의 추상 메서드를 지정하는 인터페이스이다.
추상 메서드가 하나인것에 대해 기억 해두자!
ex>추상 메서드가 하나인 함수형 인터페이스
pubilic interface Adder{
int add(int a , int b);
}
람다 표현식으로 함수형 인터페이스의 추상 메서드 구현을 직접 전달할수 있다.
ex>
Runnable r1 = () -> sout("hello world"); // sout 귀찮아서 생략..
public static void process(Runnable r){
r.run();
}
process(r1);
이런 코드가 있다고 할 때
public static void process(Runnable r){
r.run();
}
process 보면 Runnable 을 파라미터로 받고 있다.
소스를 보면 process(r1); 이라는것이 있고 r1을 정의한것을 보면 람다로 정의 되어 있다.
람다를 process라는 메소드에 파라미터로 넘긴 걸 보면
아까도 강조한 [이급 시민이었던 함수를 일급값으로 넘겨주는 프로그래밍을 함수형 프로그래밍]을 하고 있는것이다.
또 r1에는 process 안에 있는 run() 이라는 추상 메서드가 있겠구나 유추 할 수 있다.
(람다 형식인 r1 을 넘겼으니 Runnable은 함수형 인터페이스고, 함수형 인터페이스는
정확히 하나의 추상 메서드를 가지고 있다.)
또 여기서 함수형 인터페이스는 왜 하나의 추상 메서드만 가지고 있어야 하나 생각해보면
추상 메서드가 2개라면 람다로 정의 할때 () -> sout("hello world"); 이때 이게
어떤 추상 메서드에서 사용하는것인지 알수 없기 때문이 아닐까 싶다.
하나의 추상메서드만 정의 해야 람다로 정의 할때 이름을 지정하지 않아도 자동으로 동작되는게 이런 이유인것 같다.
함수형 인터페이스 사용
. Predicate 인터페이스
boolean test(T t);
. Consumer 인터페이스
void accept(T t);
.Function 인터페이스
R apply(T t);
지역 변수 사용
. 람다 표현식에서는 익명함수가 하는것 처럼 자유 변수를 활용할수 있다.
람다는 인스턴스 변수와 정적변수를 자유롭게 캡쳐 할 수 있다.
지역변수는 명시적으로 final로 선언되어 있어야 하거나 final로 선언된 변수와 똑같이 사용되어야 한다.
아래 소스는 컴파일 에러가 난다.
int port = 7333;
Runnanle r = () -> sout("port");
port = 8333;
왜 지역 변수에 이런 제약이 필요할까?
-> 인스턴스 변수는 힙에 저장, 지역변수는 스택에 저장 한다.
람다가 스레드에서 실행된다면 변수를 할당한 스레드가 사라져서 변수 할당이 해제 되었는데도
람다를 실행하는 스레드에서는 해당 변수에 접근하려할수 있다.
람다 스레드 -> 변수를 할당한 스레드에 접근 -> 스레드 접근 가능한 상황 -> 이상없음
람다 스레드 -> 변수를 할당한 스레드에 접근 -> 스레드 접근 불 가능한 상황 -> 이상
이러한 이유로 지역 변수로 직접 접근하는것이 아니라 지역 변수의 복사본을 전달하고
이 값이 바뀌지 않아야 하므로 지역 변수에는 한 번만 값을 할당 해야하는 이슈가 있다.