[Java] 19. 람다식(Lambda)를 사용하는 방법


Study/Java  2020. 5. 19. 18:04

안녕하세요. 명월입니다.


이 글은 Java에서의 람다식(Lambda)를 사용하는 방법에 대한 글입니다.


Java에서는 객체의 최소 단위가 클래스로 이루어져 있습니다. 즉, 값이 정의되지 않고 함수만 있는 프로그래밍을 만든다고 하여도 Java에서는 클래스를 만들고 안에 함수를 만들어야 합니다.

// 인터페이스
interface Testable {
  void run();
}
// Testable 인터페이스를 상속
class Test implements Testable{
  // 함수 재정의
  @Override
  public void run() {
    // 콘솔 출력
    System.out.println("run!");
  }
}
public class Example {
  // 인터페이스 Testable로 된 클래스를 실행하는 함수
  public static void test(Testable test) {
    // run 실행
    test.run();
  }
  // 실행 함수
  public static void main(String... args) {
    // 인스턴스 생성
    Testable obj = new Test();
    // test 함수 실행
    test(obj);
  }
}

이 부분은 예전에 익명 클래스에 대해서 소개할 때 설명했었습니다.

링크 - [Java] 18. 익명 클래스(Anonymous class)와 클로저(closure)


위 예제를 보면 단순히 run함수 하나를 위해서 Test 인스턴스를 생성했습니다. 자바에서 특히 안드로이드라고 생각할 때, 이벤트를 등록하고 Callback 함수를 만들 때를 생각해도 위와 같이 작성하면 됩니다.

test 함수에서 실행 로직을 작성하고 최종 Test 클래스의 run 함수를 호출하는 방식으로 말입니다.


그러나 이것이 재사용성이 많은 클래스가 아닌 일회성 클래스라고 생각한다면 Class를 생성하는 것이 오히려 관리적으로 더 힘들어 질 수 있습니다. (위와 같은 클래스가 많으면 프로젝트에 일회성 클래스가 많아질 테니)

클래스 생성을 줄일 수 있는 방법으로 익명 클래스가 있는 것입니다.


여기서 자바는 익명 클래스보다 한번 더 줄일 수 있는 방법으로 람다식(Lambda)이 있습니다.

// 인터페이스
interface Testable {
  void run();
}
public class Example {
  // 인터페이스 Testable로 된 클래스를 실행하는 함수
  public static void test(Testable test) {
    // run 실행
    test.run();
  }
  // 실행 함수
  public static void main(String... args) {
    // 람다식, 익명 클래스의 new의 선언도 없이 바로 run 함수를 실행할 수 있는 표현식
    Testable obj = () -> {
      // 콘솔 출력
      System.out.println("run!");
    };
    // test 함수 실행
    test(obj);
  }
}

익명 클래스를 람다식으로 표현할 수 있는 조건은 인터페이스에 실행 함수가 반드시 한 개이여야 합니다.

인터페이스에 실행 함수가 두 개 이상이 되면 람다식을 작성할 때 어떤 함수로 실행할지 알 수가 없어지기 때문입니다.


자바에서는 이 람다식을 만드는데 자주 사용하는 인터페이스가 두개가 있습니다.

하나는 Runnable이고 또 하나는 Callable 입니다.

보시다시피 Runnable과 Callable 인터페이스는 한 개의 함수만 존재합니다.

Runnable의 run함수는 void 형식이고 Callable는 제네릭 형식으로 리턴값을 설정합니다.

import java.util.concurrent.Callable;

public class Example {
  // Runnable를 파라미터로 한 함수
  public static void test1(Runnable test) {
    // 함수 실행
    test.run();
  }
  // Callable를 파라미터로 한 함수
  public static <V> V test2(Callable<V> test) {
    // Callable은 throws로 Exception이 설정되어 있음.
    try {
      // Callable에서 설정된 제네릭 값이 반환 타입이다.
      return test.call();
    } catch (Throwable e) {
      return null;
    }
  }

  // 실행 함수
  public static void main(String... args) {
    // test1를 람다식으로 설정한 예제
    test1(() -> {
      // 콘솔 출력
      System.out.println("run!");
    });
    // test2를 람다식으로 설정한 예제
    // Callable의 제네릭은 람다식의 return 데이터 타입에 의해 자동 설정
    int data = test2(() -> {
      // int형이기 때문에 test2의 반환 값을 int형으로 자동 설정된다.
      return 10;
    });
    // 콘솔 출력
    System.out.println("data - " + data);
  }
}

람다식에서는 익명 클래스의 기능이 그대로 사용되기 때문에 클로저 기능도 사용할 수 있습니다.

public class Example {
  // test1를 람다식으로 설정한 예제
  public static void test1(Runnable test) {
    // 함수 실행
    test.run();
  }

  // 실행 함수
  public static void main(String... args) {
    final int data = 10;
    // test1 함수에 람다식을 넣어서 실행
    test1(() -> {
      // 클로저 기능으로 data의 값을 가져와 사용할 수 있다.
      System.out.println("run! data - " + data);
    });
  }
}

람다식에서 파라미터도 설정할 수 있는데, 이는 인터페이스에서 함수의 설정대로 파라미터가 설정됩니다.

// 인터페이스
interface LambdaExpression<T, V, S> {
  // 파라미터는 모두 제네릭 타입이다.
  S run(T param1, V param2);
}
public class Example {
  // 두번째, 세번째 입력되는 데이터 타입으로 제네릭이 자동으로 결정된다.
  public static <T, V, S> S test1(LambdaExpression<T, V, S> test, T param1, V param2) {
    // LambdaExpression 인터페이스의 run 함수에 첫번째, 두번째 파라미터 넣는다.
    return test.run(param1, param2);
  }

  // 실행 함수
  public static void main(String... args) {
    // 두번째, 세번째 파라미터는 10과 20으로 V와 S는 int형으로 자동 설정
    int ret = test1((p1, p2) -> {
      // return 값은 int 형 + int 형 이기 때문에 자동으로 int형
      return p1 + p2;
    }, 10, 20);
    // 콘솔 출력
    System.out.println("Result - " + ret);
  }
}

p1와 p2는 10과 20의 값을 파라미터로 받습니다. 그리고 그 값을 더한 값을 반환하기 때문에 변수 ret에는 30의 데이터가 입력됩니다.

결과는 30의 결과가 나왔습니다.


이 람다식은 C/C++에서 함수 포인터, 함수형 프로그램에서 callback 함수와 비슷한 형태로 프로그램이 구현됩니다.

특히 안드로이드나 여러가지 라이브러리에서 이벤트 설정하는 값으로 자주 사용됩니다.


여기까지 Java에서의 람다식(Lambda)를 사용하는 방법에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.