메소드를 식으로 표현한 것이다. 메소드를 단순하게 표현한 것이다. 익명 함수라고도 부른다.
(파라미터) -> {메소드 구현}
() -> {메소드 구현}
(파라미터) -> 메소드 구현
(파라미터) -> {return 결과;}
내부에 추상 메소드가 하나만 있는 interface를 사용한다.
일급 객체(First-class Citizen)의 특성을 가진다.
내부에 구현된 추상 메소드를 클래스로 구현하지 않고 사용 할 수 있다.
public interface MyFoo{
public int myFunction(int a, int b);
}
->
를 사용해 표현한다.
(파라미터)->{메소드 구현}
형태로 사용한다.
메소드 구현이 1줄인 경우 블록을 생략 할 수 있다.
public static void main() {
MyFoo mf1 = (a, b) -> {
System.out.println(a + "+" + b);
return a + b;
};
//블록 생략
MyFoo mf2 = (a, b) -> a + b;
}
내부에 추상 메소드가 하나만 있는 interface를 함수형 인터페이스라고 부른다.
@FunctionalInterface
애노테이션을 사용하면 컴파일 단계에서 문제를 확인 할 수 있다.
2개 이상의 추상 메소드를 선언하면 컴파일 에러가 발생한다.
람다는 변수를 사용 할 때 내부적으로 값을 복사해서 사용한다. 왜 이런 방식을 사용할까?
우선 람다를 사용하면 익명 구현 객체를 사용하는 것과 비슷한 방식으로 동작한다. 정확히 내부 구현은 다르지만 힙 영역에 저장된다. 반면 지역 변수는 스택 영역에 저장된다. 그리고 메소드 실행이 끝나면 스택 영역에서 사라진다.
힙 영역에 있는 람다가 변수를 사용하려고 할 때 이 변수가 스택 영역에서 사라져있으면 변수를 사용하지 못하게 된다. 그래서 람다는 값을 복사해서 사용하는 것이다.
근데 람다를 사용하다 보면 final
변수만 사용할 수 있다. 이는 또 왜 그런걸까?
만약 다른 쓰레드에서 이 변수의 값을 변경하게 되면 정확하지 않은 결과가 나올 수 있다. 그렇기 때문에 동시성 문제를 위해 final
변수만 사용하는 것이다.
지역변수는 final이 아니기 때문에 컴파일 에러가 발생하고 인스턴스 변수는 제한 없이 사용 가능하다.
메소드, 생성자 레퍼런스는 람다를 더욱 간략하게 표현할 수 있게 해준다.
메소드를 하나만 호출하는 경우 메소드 레퍼런스를 사용하면 더 간략하게 표현 할 수 있다.
::
를 사용한다.
@FunctionalInterface
public interface MyFoo {
public int myFunction(String s);
}
public static void main() {
MyFoo mf1 = (s) -> Integer.parseInt(s);
//메소드 레퍼런스 사용
MyFoo mf2 = Integer::parseInt;
}
생성자 레퍼런스도 같은 방식으로 사용한다.
public class MyInstance {
public String s;
public MyInstance(String s) {
this.s = s;
}
}
@FunctionalInterface
public interface MyFoo {
public MyInstance myFuntion(String a);
}
public static void main() {
MyFoo mf1 = (s) -> new MyInstance(s);
//생성자 레퍼런스 사용
MyFoo mf2 = MyInstance::new;
}
https://docs.oracle.com/javase/specs/jls/se10/html/jls-15.html#jls-15.27.2