자바 8에서 도입됨
함수(메서드)를 하나의 식으로 표현하는 방법
익명함수 (anonymous function)
람다식으로 표현하면 메서드의 이름과 반환값을 생략할 수 있고, 이를 변수에 넣어 자바 코드가 매우 간결해진다.
메서드
int max(int a, int b) {
return a > b ? a : b ;
}
(a, b) -> a > b ? a : b
메서드의 이름과 반환타입을 제거하고, “→” 를 블록{} 앞에 추가한다
~~int max~~(int a, int b) -> {
return a > b ? a : b ;
}
반환값이 있는 경우 식이나 값만 적고, return문 생략 가능 (끝에 “;” 를 안붙인다)
(int a, int b) -> a > b ? a : b
매개변수의 타입이 추론 가능하면 생략 가능
(a, b) -> a > b ? a : b
( a ) -> a * a
(int a) -> a * a
a -> a * a // OK
int a -> a * a // 에러
블록 안의 문장이 하나 뿐일 때, 괄호{} 생략 가능 (끝에 “;” 를 안붙인다)
단 하나뿐인 문장이 return문이면 괄호{} 생략 불가
(int i) -> {
System.out.println(i);
}
(int i) -> System.out.println(i)
→
자바스크립트의 익명 화살표 함수 자체가 람다 함수의 일종
변수에 함수를 담을때, 자바는 강타입 언어이기에 반드시 함수에 대한 타입을 선언해야 한다.
하지만 자바에 8가지 타입밖에 없어, 마땅한 자료형이 없고, 인터페이스를 익명 구현 객체 타입으로서 함수를 해당 인터페이스 타입으로 받을수 있게 설계한것
(a, b) -> a > b ? a : b
// 오른쪽과 동일한 코드
new Object() {
int max(int a , int b) {
return a > b ? a : b ;
}
}
→ 익명 클래스, 익명 객체 : 객체의 선언과 생성을 동시에 한다.
Object obj = new Object() {
int max(int a , int b) {
return a > b ? a : b ;
}
}
타입? obj = (a, b) -> a > b ? a : b ;
⇒ 함수형 인터페이스 타입// 함수형 인터페이스가 될 수 있다.
interface OkMax {
int max(int x, int y);
}
// 함수형 인터페이스가 될수 없다.
interface NotCalculate {
int max(int x, int y);
int min(int x, int y);
}
// 구성요소가 많아도 결국 추상 메서드는 한개이기 때문에 함수형 인터페이스이다.
interface OkMax {
int max(int x, int y);
final boolean isNumber = true; // final 상수
default void print() {}; // default 메서드
static void print2() {}; // static 메서드
}
인터페이스 선언시 @FunctionalInterface
을 사용하여, 함수형 인터페이스임을 명시적으로 선언한다.
이 어노테이션을 사용하면 컴파일러가 하나의 추상 메소드만 가지는지 검증하여 잘못된 사용을 방지할 수 있다.
@FunctionalInterface
interface MyFunctionalInterface {
void execute();
}
컴파일러 스스로 람다 함수 식을 보고 타입을 유추할 수 있다.
// 함수형 인터페이스
interface OkMax {
int max(int x, int y);
}
// 람다식을 사용한 예
OkMax lambda = (a, b) -> a > b ? a : b;
람다식을 받는 메소드의 매개변수 타입을 보고, 함수형 인터페이스의 정의문을 찾아 추상 메소드의 형태를 본뒤에 추상메소드에 정의된 타입에 따라 람다 함수식의 타입을 자동으로 판별한다.
대부분의 함수형 인터페이스를 이용할때 제네릭(Generics)를 사용하게 되는데, 컴파일러가 타입을 추론하는데, 필요한 타입 정보 대부분을 제네릭에서 판별하여 얻는다.
아래 Collectors.toList() 의 정의문을 보면, 람다함수의 매개변수 타입은 제네릭T 타입이 들어있는 걸 볼수 있다.
toList() 메소드
함수형 인터페이스 | 매개변수 | 메서드 | 반환값 |
---|---|---|---|
Runnable | X | void run() | X |
Supplier | X | T get() | O |
Consumer | O | void accept(T t) | X |
Function<T, R> | O | R apply(T t) | O (R) |
Predicate | O | boolean test(T t) | O (boolean) |
함수형 인터페이스의 매개 변수 타입 == 메소드의 매개변수 타입
함수형 인터페이스의 매개 변수 개수 == 메소드의 매개변수 개수
함수형 인터페이스의 반환 타입 == 메소드의 반환 타입
정적 메서드 참조
// 기존 람다식
Function<String, Integer> stringToInteger = (s) -> Integer.parseInt(s);
// 정적 메소드 참조
Function<String, Integer> stringToInteger = Integer::parseInt;
인스턴스 메서드 참조
// 기존 람다식
BiFunction<String, String, Boolean> stringEquals = (s1, s2) -> s1.equals(s2);
// 인스턴스 메소드 참조
BiFunction<String, String, Boolean> stringEquals = String::equals;
매개변수의 메서드 참조
Function<String, Integer> size;
// 기존 람다 식
size = (String s1) -> s1.length();
// 매개변수의 메서드 참조
size = String::length;
생성자 참조
// 기존 람다식
Supplier<ArrayList<String>> listSupplier = () -> new ArrayList<>();
// 생성자 참조
Supplier<ArrayList<String>> listSupplier = ArrayList::new;
참고