Java

람다 표현식과 함수형 인터페이스

변위니 2025. 1. 7. 03:02

람다 표현식은 자바의 메서드를 하나의 식으로 간결하게 표현한 것이다.
이전에는 자바에서 메서드를 표현하려면 클래스를 정의해야 했지만, 자바 8 부터 람다식을 이용해 메서드의 이름과 반환 값을 생략할 수 있고, 이를 변수에 넣어 코드를 간결하게 한다.

 

int add (int a, int b) {
    return a + b; 
}

// 아래와 같이 변경할 수 있다.
(a, b) -> a + b;

 

 

 

(매개변수) -> {함수 본문} 의 형태로,

매개 변수가 없는 경우 () 를 사용하고, 매개 변수가 하나 인 경우 괄호를 생략할 수 있으며, 함수의 본문이 하나인 경우 중괄호와 return을 생략할 수 있다.

// 매개변수가 없고, 표현식이 하나
() -> System.out.println("hello"):

// 매개 변수가 하나
x -> x * x;

// 매개변수가 여러 개이고, 본문이 여러 줄
(x, y) -> {
    int sum = x + y;
    return sum;
}

 

 

람다식은 자바에서 함수형 프로그래밍을 할 수 있도록 도와준다. 자바에서 인터페이스는 메서드를 정의만하고 구현은 하지 않는다. 그래서 인터페이스를 사용하기 위해서는 클래스를 작성하고 메서드를 오버라이드하는 작업을 해주었다. 예로 A 인터페이스에 void run()이라는 메서드가 있으면, 이 메서드를 실제로 동작하게 할 방법이 필요하다.

람다는 이 인터페이스의 메서드를 구현하기 위해서 사용된다. Runnable 인터페이스가 있을 때, run()메서드는 구현되지 않았다.
그래서 우리가 람다를 사용해서 run()메서드를 구현할 수 있다.

Runnable.java

@FunctionalInterface
public interface Runnable {

    public abstract void run();
}

 

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

 

 

 

 

 

그러면 람다식은 어떤 특징을 가질까?

1. 우선, 람다식을 사용하면 코드가 간결하다.

// 익명 클래스로 Runnable 구현
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello, World!");
    }
};

// 람다 표현식으로 Runnable 구현
Runnable r2 = () -> System.out.println("Hello, World!");

 

 

2. 함수형 프로그래밍을 지원한다.

자바는 객체 지향 언어지만, 람다를 통해 일급 객체로 함수를 다룰 수 있다.

익명 함수란?

이름이 없는 함수로, 익명 함수는 모두 일급 객체이다. 일급 객체인 함수는 변수처럼 사용이 가능하며, 매개변수로 전달이 가능하다.

 

import java.util.function.Function;

public class LambdaExample {
    public static void main(String[] args) {
        Function<Integer, Function<Integer, Integer>> add = x -> y -> x + y;

        System.out.println(add.apply(2).apply(3));  // 출력: 5
    }
}

 

 

3. 컬렉션 작업이 간편하다.

Java 8에서는 Stream API와 함께 람다 표현식을 도입하여, 데이터 처리와 변환을 간편하게 할 수 있다.

import java.util.Arrays;
import java.util.List;

public class LambdaExample {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);

        // 병렬 처리
        list.parallelStream()
            .filter(n -> n % 2 == 0)
            .forEach(System.out::println);
    }
}

 

 

 

람다는 함수형 인터페이스를 지원한다고 한다. 그러면 함수형 인터페이스가 뭘까?
함수형 인터페이스는 단 하나의 추상 메서드를 갖는 인터페이스이다. 추상 메서드만 단 하나를 갖는다.!!! 그 외 메서드는 상관이 없다. 람다식은 이 인터페이스의 인스턴스를 생성하는데 사용된다.

 

Java8에서 제공하는 주요 함수형 인터페이스를 살펴보자.. 어떻게 사용되는지도 같이 보자.

 

 

 

Runnable

스레드를 실행하기 위한 함수형 인터페이스 

run()추상 메서드를 갖고, 매개변수를 받지 않으며 반환값이 없다.

 

 

Thread 클래스의 생성자 중 하나로 Runnable 객체를 매개변수로 받아 스레드의 실행 작업을 정의하는데 사용된다.

아래와 같이 구현할 수 있다.

public class RunnableExample {
    public static void main(String[] args) {
        // Runnable 인터페이스를 람다식으로 구현
        Runnable task = () -> System.out.println("Task is running in a thread");

        // Runnable을 스레드에 전달하고 실행
        Thread thread = new Thread(task);
        thread.start();
    }
}

 

 

 

 

Function<T, R>

T 타입의 매개변수를 입력 받아, R을 리턴한다.

  • R apply (T t)
@FunctionalInterface
public interface Function<T, R> {
  R apply(T t);
}

map 메서드의 매개변수로 전달된다. 스트림의 각 요소를 Function을 사용해 변환한 후 새로운 스트림을 반환한다.

Function<? super T, ? extends R> mapper는 변환을 수행하는 함수로, 스트림의 각 요소(T)를 받아 새로운 값(R)을 생성한다.

public class MapExample {

    public static void main(String[] args) {

        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        List<Integer> squares = numbers.stream()
            .map(n -> n * n)
            .collect(Collectors.toList());
    }
}

 

여기서 map(n -> n * n) 에서 n의 타입이 명시되지 않았지만, 컴파일러는 numbers 리스트가 Integer 인 것을 알고 있기 때문에 n도 Integer라고 추론한다. 이걸 타입 추론이라고 한다.
입력을 받아서 출력으로 변환하며, 주로 변환 작업에 사용된다.

 

 

 

Predicate< T >

T타입의 매개변수를 입력 받아, 조건 검사 후 boolean 값을 반환한다.

  • boolean test(T t)

입력 값에 대해 boolean 값을 반환하며, 주로 조건 검증에 사용한다.

 

 

Consumer< T >

T타입의 입력을 받아 동작을 수행한 후, 결과를 반환하지 않는다.

  • void accept(T t)

입력을 받아서 동작을 수행하며, 결과를 반환하지 않는다.

 

 

Supplier< T >

매개변수 없이 T타입의 값을 반환하는 함수형 인터페이스로 객체를 생성하거나 값을 제공한다.

  • T get()

매개 변수를 받지 않고, 값을 생성하거나 제공한다.

 

 

'Java' 카테고리의 다른 글

Thread 구현 동기화와 Lock  (0) 2025.01.07
람다와 함수형 프로그래밍의 원리와 활용  (0) 2025.01.07
@Transactional과 Proxy  (0) 2025.01.07
Transaction  (0) 2025.01.07
@RequiredArgsConstructor, @AllArgsConstructor  (0) 2025.01.07