람다(Java 8+)는 간결한 익명 함수이며, 함수형 인터페이스는 정확히 하나의 추상 메서드를 가진 인터페이스입니다 — 람다가 구현하는 대상 타입입니다. 이들이 함께 Java에 함수형 프로그래밍을 가져왔고 Stream API를 뒷받침합니다.
함수형 인터페이스
{
;
}
하나의 추상 메서드를 가진 인터페이스는 "함수형"입니다 — 람다가 어느 메서드를 나타내는지에 대한 모호함이 없으므로 람다가 이를 구현할 수 있습니다.
// ❌ 장황한 익명 클래스 (Java 8 이전)
Calculator add = new Calculator() {
public int calculate(int a, int b) { return a + b; }
};
// ✅ 람다 — 같은 것, 훨씬 적은 보일러플레이트
Calculator add = (a, b) -> a + b;
Calculator multiply = (a, b) -> a * b;
add.calculate(2, 3); // 5
람다 (a, b) -> a + b는 단일 메서드의 구현을 제공합니다 — 클래스도, 메서드 시그니처 보일러플레이트도 없습니다.
Function<String, Integer> length = s -> s.length(); // T 를 받고 R 을 반환
Predicate<Integer> isPositive = n -> n > 0; // T 를 받고 boolean 반환
Consumer<String> printer = s -> System.out.println(s); // T 를 받고 void 반환
Supplier<String> greeting = () -> "Hello"; // 아무것도 받지 않고 T 반환
BiFunction<Integer, Integer, Integer> sum = (a, b) -> a + b; // 두 인자
Java는 흔한 형태를 위한 미리 만들어진 함수형 인터페이스를 제공하며, 표준 라이브러리 전반(특히 stream)에서 사용됩니다.
// 람다가 단지 기존 메서드를 호출할 때는 메서드 참조를 사용
list.forEach(System.out::println); // x -> System.out.println(x) 대신
list.stream().map(String::toUpperCase); // s -> s.toUpperCase() 대신
list.stream().map(Integer::parseInt); // static 메서드 참조
// 동작을 인자로 전달 — Stream API 의 핵심 동력
people.stream()
.filter(p -> p.getAge() > 18) // Predicate 람다
.map(Person::getName) // 메서드 참조
.forEach(System.out::println); // Consumer
list.sort((a, b) -> a.compareTo(b)); // Comparator 람다
button.addActionListener(e -> handleClick()); // 이벤트 핸들러
람다와 함수형 인터페이스는 Java에 함수형 프로그래밍을 가져와 현대 Java를 작성하는 방식을 근본적으로 바꿨습니다.
이들은 동작을 데이터로 전달하기 위한 장황한 익명 클래스 보일러플레이트를 제거하여, 간결한 이벤트 핸들러, comparator, 콜백, 그리고 — 가장 중요하게는 — 전체 Stream API(filter/map/reduce 연산에 람다에 의존)를 가능하게 합니다.
람다가 함수형 인터페이스(하나의 추상 메서드)를 대상으로 한다는 것, 내장 인터페이스(Function, Predicate, Consumer, Supplier), 그리고 메서드 참조를 이해하는 것은 관용적인 현대 Java를 작성하고 stream, 컬렉션, 동시성 API를 효과적으로 사용하는 데 필수적입니다.
이들은 언어의 주요한 진화를 나타내며 현재 Java 능숙함의 핵심입니다.