람다식이란?
메서드를 하나의 식으로 표현한 것이다. 람다식은 함수를 간략하고 명확하게 표현하게 해준다.
람다식으로 표현하면 메서드의 이름과 반환값이 없어지므로, '익명 함수'라고도 한다.
람다식 작성하기
// 두 값 중에서 큰 값을 반환하는 메서드
int max(int a, int b){
return a > b ? a : b;
}
// 아래와 같이 표현할 수 있다.
(int a, int b){
return a > b ? a : b;
}
반환값이 있는 메서드의 경우, return문 대신 식으로 대신 할 수 있다. 식의 연산 결과가 자동적으로 반환값이 된다.
이때는 문장이 아닌 식이므로 끝에 ';'을 붙이지 않는다.
(int a, int b) -> {return a > b ? a : b;} -----> (int a, int b) -> a > b ? a : b
선언된 매개변수의 타입은 추론이 가능한 경우 생략 가능하다.
(int a, int b) -> a > b ? a : b -----> (a, b) -> a > b ? a : b
선언된 매개변수가 하나일 경우 괄호를 생략할 수 있다. 단, 매개변수의 타입이 있으면 괄호를 생략할 수 없다.
(a) -> a * a a -> a * a // OK
(int a) -> a * a ------> int a - > a * a //에러
괄호 안의 문장이 하나일 때는 괄호를 생략할 수 있다. 이때 문장의 끝에 ';'를 생략해야 한다.
(String name, int i ) -> { (String name, int i) ->
System.out.println(name + "=" + i); -----> System.out.println(name + "=" + i)
}
그러나 괄호 안의 문장이 return문일 경우 괄호를 생략할 수 없다.
(int a, int b) -> { return a > b ? a : b;} //OK
(int a, int b) -> return a > b ? a : b //에러
함수형 인터페이스
max()라는 메서드가 정의된 MyFunction인터페이스가 정의되어 있다고 가정하자.
public interface MyFunction {
public abstract int max(int a, int b);
}
MyFunction f = new MyFunction() { // 익명 객체
public int max(int a, int b) {
return a > b ? a : b;
}
};
int big = f.max(5,3); // 익명 객체의 메서드를 호출
위 코드의 익명 객체를 함다식으로 아래와 같이 대체할 수 있다.
MyFunction f = (int a, int b) -> a > b ? a : b;// 익명 객체를 람다식으로 대체
int big = f.max(5,3); // 익명 객체의 메서드를 호출
하나의 메서드가 선언된 인터페이스를 정의해서 람다식을 다루는 것은 기존의 자바의 규칙들을 어기지 않으면서도 자연스럽다. 그래서 인터페이스를 통해 람다식을 다루기로 결정되었으며, 람다식을 다루기 위한 인터페이스를 '함수형 인터페이스'라고 부른다.
@FunctionalInterface
public interface MyFunction {
public abstract int max(int a, int b);
}
주의할 점은 오직 하나의 추상 메서드만 정의되어 있어야 한다는 것이다. 그래야 람다식과 인터페이스의 메서드가 1:1로 연결될 수 있기 때문이다.
Java.util.function 패키지
일반적으로 자주 쓰이는 형식의 메소드를 함수형 인터페이스로 미리 정의해놓았다. 매번 새로운 함수형 인터페이스를 정의하지 말고, 가능하면 이 패키지의 인터페이스를 활용하는 것이 좋다. ('T'는 Type, 'R'은 Return Type을 의미한다.)
함수형 인터페이스 | 메서드 | 설명 |
java.lang.Runnable | run() | 매개변수 X, 반환값 X |
Supplier<T> | get() | 매개변수 X, 반환값 O |
Consumer<T> | accept(T t) | 매개변수 O, 반환값 X |
Function<T,R> | apply(T t) | 하나의 매개변수를 받아 결과 반환 |
Predicate<T> | test(T t) | 조건식을 표현하는데 사용 매개변수는 하나, 반환 타입은 boolean |
매개변수가 두 개인 함수형 인터페이스
함수형 인터페이스 | 메서드 | 설명 |
BiConsumer<T,U> | accept<T t, U u> | 두 개의 매개변수만 있고 반환값 없음 |
BiPredicate<T,U> | test<T t, U u> | 조건식 표현에 사용, 매개변수 둘, 반환값은 boolean |
BiFunction<T,U,R> | apply(T t, U u) | 두 개의 매개변수를 받아 하나의 결과로 반환 |
기본형을 사용하는 함수형 인터페이스
보다 효율적으로 처리할 수 있도록 기본형을 사용하는 함수형 인터페이스들이 제공된다.
함수형 인터페이스 | 메서드 | 설명 |
DoubleToIntFunction | applyAsInt(double d) | AToBFunction은 입력이 A타입 출력이 B타입 |
ToIntFunction<T> | applyAsInt(T value) | ToBFunction은 출력이 B이다. 입력은 제네릭 타입 |
intFunction<R> | apply(T t, U u) | AFunction은 입력이 A, 출력은 제네릭 타입 |
ObjectConsumer<T> | accept(T t, U u) | ObjAFunction은 입력이 T, A 타입 출력은 없다. |
메서드 참조
'메서드 참조'라는 방법으로 람다식을 간략히 할 수 있다.
Function<String, Integer> f= (String s) -> Integer.parseInt(s);
↓
Function<String, Integer> f= Integer::parseInt;
위 메서드 참조에서 람다식의 일부가 생략되었지만, 컴파일러는 생략된 부분을 우변의 parseInt메서드의 선언부로부터, 또는 좌변의 Function인터페이스에 지정된 제네릭 타입으로부터 쉽게 알아낼 수 있다.
종류 | 람다 | 메서드 참조 |
static 메서드 참조 | (x) -> ClassName.method(x) | ClassName::method |
인스턴스메서드 참조 | (obj,x) -> obj.method(x) | ClassName::method |
특정 객체 인스턴스메서드 참조 | (x) -> obj.method(x) | obj::method |
하나의 메서드만 호출하는 람다식은 '클래스이름::메서드이름' 또는 '참조변수::메서드이름'으로 바꿀 수 있다.
생성자의 메서드 참조
Supplier<MyClass> s = () -> new MyClass(); // 람다식
Supplier<MyClass> s = MyClass::new; // 메서드 참조
매개변수가 있는 생성자라면, 매개변수의 개수에 따라 알맞은 함수형 인터페이스를 사용하면 된다.
Function<Integer,MyClass> f = (i) -> new MyClass(i); // 람다식
Function<Integer,MyClass> f = MyClass::new; // 메서드 참조
배열을 생성할 때는 아래와 같이 한다.
Function<Integer,int[]> f = x -> new int[x]; // 람다식
Function<Integer,int[]> f = int[]::new; // 메서드 참조
'Language > Java' 카테고리의 다른 글
[Java]스트림(stream)(2/3) (0) | 2022.06.07 |
---|---|
[Java]스트림(stream)(1/3) (0) | 2022.06.07 |
예외처리(Exception Handling) (0) | 2022.05.27 |
객체 지향 프로그래밍에 대해 묻는다면 ? (0) | 2022.05.25 |
Java는 어떻게 동작하지?( + 메모리 영역) (0) | 2022.05.23 |