자바 람다식(Lambda Expression)과 메서드 참조(Method Reference)

coldrice99·2024년 9월 9일

1. 람다식 (Lambda Expression)

람다식이란?

람다식은 자바 8에서 도입된 익명 함수로, 메서드를 간결하게 표현하는 방식이다. 메서드 이름과 반환형, 접근 제어자 등을 쓰지 않고도, 함수를 간단하게 작성할 수 있어 코드 가독성이 높아진다.

2. 람다식의 구조

람다식은 기본적으로 다음과 같은 형태를 가진다:

(매개변수) -> { 실행할 코드 }

이것을 더 구체적으로 설명해 보면:

  • 매개변수: 메서드처럼 입력 값을 받는 부분이다.
  • ->: 화살표 연산자는 람다식에서 매개변수와 실행할 코드를 구분한다.
  • { 실행할 코드 }: 매개변수를 가지고 처리할 실제 코드이다.

예시:

기본적인 람다식 예시로, 두 숫자를 더하는 메서드를 람다식으로 표현할 수 있다.

(int a, int b) -> { return a + b; }

3. 람다식의 기본 예시

기존 방식과 비교해 보자.

기존 방식:

public interface Adder {
    int add(int a, int b);
}

Adder adder = new Adder() {
    @Override
    public int add(int a, int b) {
        return a + b;
    }
};
System.out.println(adder.add(5, 3)); // 출력: 8

이 코드는 인터페이스를 구현한 익명 클래스를 생성해서 두 수를 더하는 코드다. 이 코드를 람다식으로 바꾸면 다음과 같다.

람다식 사용:

Adder adder = (a, b) -> a + b;
System.out.println(adder.add(5, 3)); // 출력: 8

4. 매개변수와 코드 블록 생략

람다식은 다음과 같은 규칙을 가지고 있어 더 간결하게 작성할 수 있다:

  1. 매개변수가 하나일 때는 괄호 생략 가능

    • 매개변수가 하나라면 괄호를 생략할 수 있다.
    Consumer<String> printer = s -> System.out.println(s);
  2. 코드가 한 줄일 때는 중괄호와 return 생략 가능

    • 코드 블록이 한 줄일 경우 중괄호와 return을 생략할 수 있다.
    (a, b) -> a + b

5. 람다식 사용의 전제 조건: 함수형 인터페이스

람다식을 사용하려면 함수형 인터페이스가 필요하다. 함수형 인터페이스는 단 하나의 추상 메서드만 가지고 있는 인터페이스다. 자바에서 제공하는 몇 가지 함수형 인터페이스를 알아보자:

  • Runnable: 인자가 없고 반환값도 없는 함수형 인터페이스
  • Comparator<T>: 두 개의 값을 비교하는 함수형 인터페이스
  • Predicate<T>: 조건을 테스트해 true 또는 false를 반환하는 함수형 인터페이스

6. 람다식의 예시

1) Runnable 인터페이스 사용 예시:
기본적으로 자바에서 Runnable은 인자가 없고, 반환값도 없는 함수형 인터페이스이다. 람다식을 사용하면 더욱 간결해진다.

Runnable task = () -> System.out.println("Hello, Lambda!");
task.run();

2) Comparator 인터페이스 사용 예시:
Comparator는 두 객체를 비교하는 함수형 인터페이스다. 람다식을 사용하여 숫자를 비교하는 방법은 다음과 같다.

Comparator<Integer> comparator = (a, b) -> a - b;
int result = comparator.compare(3, 2);
System.out.println(result); // 출력: 1 (3이 2보다 큼)

3) Predicate 인터페이스 사용 예시:
Predicate는 특정 조건을 검사하고, true 또는 false를 반환하는 함수형 인터페이스다.

Predicate<String> isEmpty = s -> s.isEmpty();
System.out.println(isEmpty.test("")); // 출력: true

2. 메서드 참조 (Method Reference)

람다식과 함께 자바 8에서 도입된 또 다른 중요한 기능은 메서드 참조이다. 메서드 참조는 람다식에서 사용되는 메서드 호출을 간결하게 표현하기 위해 사용된다. 메서드 참조는 :: 기호를 사용하여 작성된다.

메서드 참조의 형태

메서드 참조는 아래와 같은 방식으로 표현된다:

  • 정적 메서드 참조: 클래스명::메서드명
  • 인스턴스 메서드 참조: 인스턴스명::메서드명
  • 생성자 참조: 클래스명::new

메서드 참조의 예시

1) 정적 메서드 참조

정적 메서드를 참조하는 방식이다. 예를 들어 String.valueOf() 메서드를 메서드 참조로 표현하면 아래와 같다.

Function<Integer, String> func = String::valueOf;
System.out.println(func.apply(123)); // 출력: "123"

2) 인스턴스 메서드 참조

인스턴스의 메서드를 참조하는 방식이다. 다음은 System.out.println() 메서드를 메서드 참조로 표현한 예시다.

Consumer<String> printer = System.out::println;
printer.accept("Hello, Method Reference!"); // 출력: Hello, Method Reference!

3) 생성자 참조

객체의 생성자를 참조하는 방식이다. 아래는 ArrayList의 생성자를 메서드 참조로 표현한 예시다.

Supplier<List<String>> listSupplier = ArrayList::new;
List<String> list = listSupplier.get();

메서드 참조와 람다식 비교

람다식과 메서드 참조는 매우 비슷하지만, 메서드 참조는 더 간결하게 표현할 수 있다. 아래는 람다식과 메서드 참조를 비교한 예시이다.

람다식 사용:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// 람다식으로 대문자로 변환
names.stream()
     .map(name -> name.toUpperCase())
     .forEach(name -> System.out::println);

메서드 참조 사용:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// 메서드 참조로 대문자로 변환
names.stream()
     .map(String::toUpperCase)
     .forEach(System.out::println);

메서드 참조는 단순히 메서드를 호출하는 람다식의 경우 더 간결하게 사용할 수 있다.


2. 메서드 참조 (Method Reference)

메서드 참조람다식을 간결하게 표현하는 방법이다. 람다식에서 특정 메서드를 호출하는 경우, 메서드 참조를 통해 해당 메서드를 바로 참조할 수 있다. 메서드 참조는 자바 8에서 도입되었으며, :: 연산자를 사용한다.

메서드 참조의 종류

메서드 참조는 다음과 같이 4가지 형태로 나뉜다:

  1. 정적 메서드 참조: 클래스의 정적 메서드를 참조할 때 사용한다.

    • 형식: 클래스명::메서드명
    • 예: String::valueOf
  2. 인스턴스 메서드 참조: 특정 객체의 인스턴스 메서드를 참조할 때 사용한다.

    • 형식: 인스턴스명::메서드명
    • 예: System.out::println
  3. 임의 객체의 인스턴스 메서드 참조: 특정 클래스의 임의의 객체의 인스턴스 메서드를 참조할 때 사용한다.

    • 형식: 클래스명::메서드명
    • 예: String::toUpperCase
  4. 생성자 참조: 객체 생성자를 참조할 때 사용한다.

    • 형식: 클래스명::new
    • 예: ArrayList::new

3. 람다식과 메서드 참조 비교

람다식과 메서드 참조는 서로 대체할 수 있는 개념이다. 메서드 참조는 더 간결하게 메서드를 호출할 수 있게 해준다.

예시 1: 정적 메서드 참조

람다식 사용:

Function<Integer, String> func = (i) -> String.valueOf(i);

메서드 참조 사용:

Function<Integer, String> func = String::valueOf;

두 코드는 동일하게 작동하지만, 메서드 참조는 더 간결한 표현이다.

예시 2: 인스턴스 메서드 참조

람다식 사용:

Consumer<String> printer = (str) -> System.out.println(str);

메서드 참조 사용:

Consumer<String> printer = System.out::println;

이 코드 역시 메서드 참조가 훨씬 간단하게 표현된다.

4. 메서드 참조의 예시

예시 1: 리스트의 숫자를 문자열로 변환하기

람다식 사용:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<String> strings = numbers.stream()
                              .map(n -> String.valueOf(n))
                              .collect(Collectors.toList());
System.out.println(strings); // 출력: [1, 2, 3, 4, 5]

메서드 참조 사용:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<String> strings = numbers.stream()
                              .map(String::valueOf)
                              .collect(Collectors.toList());
System.out.println(strings); // 출력: [1, 2, 3, 4, 5]

예시 2: 리스트의 문자열을 대문자로 변환하기

람다식 사용:

List<String> names = Arrays.asList("alice", "bob", "charlie");
List<String> upperNames = names.stream()
                               .map(name -> name.toUpperCase())
                               .collect(Collectors.toList());
System.out.println(upperNames); // 출력: [ALICE, BOB, CHARLIE]

메서드 참조 사용:

List<String> names = Arrays.asList("alice", "bob", "charlie");
List<String> upperNames = names.stream()
                               .map(String::toUpperCase)
                               .collect(Collectors.toList());
System.out.println(upperNames); // 출력: [ALICE, BOB, CHARLIE]

5. 요약

  • 람다식은 메서드를 간결하게 표현하는 방식이다. 함수형 인터페이스와 함께 사용된다.
  • 메서드 참조는 이미 존재하는 메서드를 간단히 참조하여 더 간결하게 람다식을 표현할 수 있는 방법이다. :: 연산자를 사용하여 클래스나 인스턴스의 메서드를 참조한다.

람다식과 메서드 참조는 자바 8에서 도입된 중요한 기능으로, 코드 가독성과 간결함을 높일 수 있다.


profile
서두르지 않으나 쉬지 않고

0개의 댓글