람다 표현식은 메서드로 전달할 수 있는 익명 함수를 단순화 한 것이라고 할 수 있다.
(Test t1, Test t2) -> t1.getLength().compareTo(t2.getLength());
함수형 인터페이스라는 문맥에서 람다 표현식을 사용할 수 있다.
하나의 추상 메서드를 지정하는 인터페이스를 뜻한다.
예시로 Java API의 함수형 인터페이스로 Comparator,Runnable 등이 있다.
람다 표현식으로 함수형 인터페이스의 추상 메서드를 구현을 직접 전달할 수 있으므로 전체 표현식을 함수형 인터페이스의 인스턴스로 취급할 수 있습니다.
@FunctionalInterface 어노테이션을 통하여 해당 인터페이스가 함수형 인터페이스임을 가리킬 수 있으며 실제로 추상 메서드가 한개 이상일 경우 에러를 발생시킵니다.
람다 표현식의 시그니처를 서술하는 메서드를 함수 디스크립터라고 칭합니다.
예를 들어 Runnable의 유일한 추상 메서드 run은 인수와 반환값이 없으므로 이를 시그니처로 생각 할 수 있습니다.
() -> void
Java에서는 기본적으로 여러 시그니처 유형에 함수형 인터페이스를 제공해줍니다.
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
public <T> List<T> filter(List<T> list, Predicate<T> p) {
List<T> results = new ArrayList<>();
for (T t : list) {
if (p.test(t))
resuls.add(t);
}
return results;
}
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listsOfStrings, nonEmptyStringPredicate);
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
public void <T> void forEach(List<T> list, Consumer<T> c) {
for (T t : list) {
c.accept(t);
}
}
forEach(
Arrays.asList(1,2,3,4,5),
(Integer i) -> System.out.println(i); // <- Consumer의 accept 메서드를 구현하는 람다
);
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
public <T, R> List<R> map(List<T> list, Function<T, R> f) {
List<R> result = new ArrayList<>();
for (T t : list) {
result.add(f.apply(t));
}
return result;
}
List<Integer> l = map(
Arrays.asList("lambdas", "in", "action"),
(String s) -> s.length(); // <- Function의 apply 메서드를 구현하는 람다
);
List<Apple> heavierThan150gram =
filter(inventory, (Apple apple) -> apple.getWeitht() > 150);
Comparator<Apple> c =
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
Comparator<Apple> c =
(a1, a2) -> a1.getWeight().compareTo(a2.getWeight());
인스턴스 변수는 heap영역, 지역 변수는 stack영역에 위치한다.
람다가 스레드에서 실행된 후, 변수를 할당한 스레드가 사라져 stack 메모리에서 해제되었으나, 람다를 실행하는 스레드는 살아 있어 해당 변수에 접근을 요청할 케이스가 생긴다.
실제 변수에 접근을 허용하는 것이 아닌 해당 변수의 복사본을 만들어 그 곳에 접근을 허용하는데 이를 람다 캡처링 이라고 한다.
복사한 값과 원본의 값이 달라지면 안되기 때문에, 람다에서 접근하는 지역 변수는 final로 선언되어야 한다.
메서드 참조로 가독성을 높일수 있습니다.
(args) -> ClassName.staticMethod(args)
ClassName::staticMethod
(arg0, rest) -> arg0.instanceMethod(rest)
ClassName::instanceMethod
(args) -> expr.instanceMethod(args)
expr::instanceMethod