| 종류 | 문법 | 같은 뜻의 람다 | 예 |
|---|---|---|---|
| static 메서드 | 클래스::메서드 |
x -> 클래스.메서드(x) |
Integer::parseInt |
| 특정 객체의 인스턴스 메서드 | 객체::메서드 |
x -> 객체.메서드(x) |
prefix::concat, System.out::println |
| 임의 객체의 인스턴스 메서드 | 클래스::메서드 |
x -> x.메서드() |
String::length, String::toUpperCase |
| 생성자 | 클래스::new |
() -> new 클래스() |
ArrayList::new |
1번과 3번은 문법이 같아서(클래스::메서드) 헷갈립니다. 구분은 메서드가 static인가입니다. Integer::parseInt는 static이라 첫 인자가 매개변수로, String::length는 인스턴스 메서드라 첫 인자가 수신 객체(this)가 됩니다. String::compareTo처럼 인자가 둘이면 (a, b) -> a.compareTo(b)입니다.
함수를 인자로 받거나 함수를 반환하는 메서드입니다. 자바에서는 함수형 인터페이스 타입의 매개변수·반환 타입으로 표현합니다.
| 방향 | 시그니처 예 | 용도 |
|---|---|---|
| 받기 | T applyTwice(UnaryOperator<T> f, T x) |
전략 주입, 콜백, 파이프라인 단계 |
| 반환 | IntUnaryOperator adder(int n) |
팩토리, 커링, 부분 적용 |
| 받고 반환 | Supplier<T> retry(int n, Supplier<T> action) |
데코레이터(재시도, 캐시, 로깅, 시간 측정) |
함수를 반환할 때 람다는 바깥 메서드의 매개변수(n)를 캡처합니다. 메서드가 끝나 스택 프레임이 사라져도 람다는 값을 복사해 갖고 있으므로(effectively final 제약이 여기서 나옵니다) adder(5)가 돌려준 함수는 언제 불러도 5를 더합니다.
f.andThen(g) = x -> g(f(x)) // f 먼저, 그다음 g (읽는 순서 = 실행 순서)
f.compose(g) = x -> f(g(x)) // g 먼저, 그다음 f (수학의 f∘g)andThen이 왼쪽에서 오른쪽으로 읽히므로 실무에서는 거의 andThen만 씁니다. Predicate는 and/or/negate, Comparator는 thenComparing/reversed가 같은 역할입니다.
| 용어 | 뜻 | 형태 |
|---|---|---|
| 커링(currying) | 인자 n개 함수를 "인자 1개를 받아 나머지를 받는 함수를 돌려주는" 함수로 변환 | (a, b) -> c → a -> (b -> c) |
| 부분 적용(partial application) | 인자 일부를 미리 고정한 새 함수 | (a, b) -> c, a=3 → b -> c |
자바에는 내장 커링이 없지만 Function<A, Function<B, C>>로 표현합니다. 실무에서는 "설정값을 미리 묶은 함수"(partial(greet, "Hello"))로 자주 쓰입니다. 람다 문법에서 a -> b -> f.apply(a, b)는 a -> (b -> f.apply(a, b))로 오른쪽부터 묶입니다.
static <T> T firstOr(List<T> list, T fallback)
// └ 타입 매개변수 선언: 반환 타입 앞컴파일러는 인자의 타입과 대입되는 변수의 타입(target typing)에서 T를 추론합니다. 추론이 실패하는 경우(인자가 없고 대입도 없을 때, 람다 인자만 있을 때)는 Main.<String>emptyOf()처럼 점 앞에 타입 인자를 명시합니다. 이 문법은 반드시 클래스. 또는 this.가 앞에 있어야 합니다.
SuppliervalueOrDefault(v, expensive())는 v가 있어도 expensive()가 먼저 실행됩니다(인자는 호출 전에 평가). valueOrDefault(v, () -> expensive())는 필요할 때만 get()을 부릅니다. Optional.orElse vs orElseGet, 로깅의 log.debug(() -> ...), Stream의 중간 연산이 모두 같은 원리입니다.
CompletableFuture 체이닝 3종| 메서드 | 인자 함수의 형태 | 언제 |
|---|---|---|
thenApply(f) |
T -> U |
결과를 동기 변환 (map) |
thenCompose(f) |
T -> CompletableFuture<U> |
결과로 또 다른 비동기 작업 시작 (flatMap) |
thenCombine(other, f) |
(T, U) -> V |
독립된 두 작업이 모두 끝나면 합치기 |
thenApply에 CompletableFuture를 돌려주는 함수를 넣으면 CompletableFuture<CompletableFuture<U>>가 됩니다. 그것을 펴 주는 것이 thenCompose입니다. Optional.map/flatMap, Stream.map/flatMap과 정확히 같은 구분입니다.