본문 바로가기

모던자바인액션

[Part1] Chapter3. 람다

1. 람다

익명함수의 일종. 

1.1. 람다의 특징

익명 

보통의 메서드와 달리 이름이 없으므로 람다를 익명이라 표현한다. 익명이므로 구현해야 할 코드가 줄어든다.

 

함수 

람다는 메서드처럼 특정 클래스에 종속되지 않으므로 함수라고 부른다.

하지만 메서드처럼 파라미터 리스트, 바디, 반환 형식, 가능한 예외 리스트를 포함한다.

 

전달

람다 표현식을 메서드 인수로 전달하거나 (동작 파라미터화) 변수로 저장할 수 있다.

 

간결성

익명클래스 처럼 많은 자질구레한 코드를 구현할 필요가 없다.

1.2. 람다의 구조

//람다는 return이 함축되어 있음
 int slen = (String s) -> s.length();
 
//람다 바디 중괄호 안에 여러 행의 문장을 포함할 수 있음
(int x, int y) -> {
    System.out.println("Result:");
    System.out.println(x + y);
}

//람다의 파라미터가 없을 수도 있다. 이때는 42를 그대로 반환
() -> 42

1.3 람다의 사용

함수형 인터페이스라는 문맥에서 람다 표현식을 사용할 수 있다.

2. 함수형 인터페이스 

정확히 하나의 추상 메서드를 지정하는 인터페이스.

람다표현식으로 함수형 인터페이스의 추상 메서드 구현을 직접 전달한다.

람다로 함수형 인터페이스의 인스턴스를 만들 수 있다.

@FuntionalInterface 어노테이션으로 이를 명시할 수 있음.

 

아래는 자바API의 대표적인 함수형 인터페이스인 Runnable이다.

public interface Runnable {
    void run();
}
// 람다 사용
Runnable r1 = () -> System.out.println("Hello World 1");

// 익명 클래스 사용
Runnable r2 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello World 2");
    }
};

// 람다로 파라미터 전달
public void process(Runnable r3) {
    r3.run();
}

process(() -> System.out.println("Hello World 3"));

3. 함수 디스크립터

함수형 인터페이스의 추상 메서드 시그니처.

* 메서드 시그니처? 메서드 이름과 매개변수 리스트의 조합.

동일한 함수 디스크립터는 재활용이 가능하다. 코드 중복을 줄일 수 있는 방법 !

3.1. Predicate

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);
}

3.2. Consumer

@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);
}

3.3. Function

@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);
}

4. 형식 검사 / 형식 추론

람다 표현식 자체에는 람다가 어떤 함수형 인터페이스를 구현하는지의 정보가 포함되어있지 않기 때문에, 형식을 추론 / 파악하여야 한다.

4.1. 형식 검사 

람다가 사용되는 문맥(콘텍스트)을 이용하여 형식을 추론할 수 있다.

어떤 문맥(콘텍스트)에서 기대되는 람다 표현식의 형식을 "대상형식"이라고 부른다. 

(대상형식 = 람다 표현식의 기대 형식)

List<Apple> heavierThan150g = filter(inventory, (Apple apple) -> apple.getWeight() > 150);

- filter 메서드의 선언을 확인한다. 

- filter 메서드는 두 번째 파라미터로 Predicate<Apple> 형식(대상 형식)을 기대한다.

- Predicate<Apple>은 test라는 한 개의 추상 메서드를 정의하는 함수형 인터페이스다.

- test 메서드는 Apple을 받아 boolean을 반환하는 함수 디스크립터를 묘사한다.

- filter 메서드로 전달된 인수는 이와 같은 요구사항을 만족해야 한다.

 

 

4.2. 형식 추론

대상 형식을 이용하여 람다 표현식과 관련된 함수형 인터페이스를 추론한다.

대상 형식 -> 함수 디스크립터 -> 람다의 시그니처 추론.

 

컴파일러가 추론할 수 있는 영역은 생략할 수 있다.

4.3. 람다 캡처링

람다 표현식에서 외부에서 정의된 변수(자유변수)를 사용함.

final으로 선언된 변수만 사용가능(혹은 실질적인 final). 한 번만 할당할 수 있는 지역변수를 캡처할 수있다.

5. 메서드 참조 

특정 람다 표현식을 축약한 것.

새로운 기능이 아니라, 하나의 메서드를 참조하는 람다를 편리하게 표현할 수 있는 문법이다.

6. 생성자 참조

// 인수없는 생성자
Supplier<Apple> c1 = Apple::new;
Apple a1 = c1.get();

Supplier<Apple> c2 = () -> new Apple();
Apple a2 = c2.get();

// weight를 인수로 가지는 생성자
Function<Integer, Apple> c3 = Apple::new;
Apple a3 = c3.apply(100);

// 두개의 인수를 가지는 생성자
BiFunction<Color, Integer, Apple> c4 = Apple::new;
Apple a4 = c4.apply(Color.GREEN, 110);

7. 람다 표현식 조합

함수형 인터페이스는 디폴트 메서드를 무제한으로 가질 수 있기 때문에, 이를 활용하여 람다를 조합할 수 있다.

8. 결론

- 람다 표현식: 익명함수의 일종

- 함수형 인터페이스: 하나의 추상메서드만을 정의하는 인터페이스

- 람다표현식 - 함수형인터페이스 관계

   1) 함수형 인터페이스를 기대하는 곳에서만 람다 표현식을 사용할 수 있음.

   2) 람다 표현식 전체가 함수형 인터페이스의 인스턴스로 취급됨.