# Coding/Java

자바 람다식

hxodoo.cookie 2023. 10. 12. 01:55

자바 8부터 도입된 람다식은 혁신적인 프로그래밍 패러다임을 제시하며 자바 개발을 더욱 간결하고 효율적으로 만들었습니다. 이 글에서는 자바 람다식에 대해 상세하게 알아보겠습니다.

 

 

자바 람다식

람다식은 함수형 프로그래밍의 핵심 개념 중 하나로, 익명 함수(Anonymous Function)의 일종입니다. 간단하게 말하면, 람다식은 메서드를 하나의 식(expression)으로 표현한 것입니다. 자바에서는 메서드를 일급 객체로 다룰 수 있도록 하고, 코드를 간결하게 표현할 수 있도록 람다식을 도입했습니다.

 

 

 

람다식은 다음과 같은 구조를 갖습니다.

 

(parameter) -> expression

 

 

:: 여기서 (parameter)는 메서드의 파라미터를 나타내며, ->는 람다식의 화살표(arrow)로, expression은 메서드의 본문을 나타냅니다.

 

 

 

예시 코드: 두 숫자를 더하는 메서드를 표현한 람다식

 

 

(int a, int b) -> a + b

 

 

 

 

람다식이 필요한가?

 

▶ 코드 간결성

 

람다식의 가장 큰 장점 중 하나는 코드의 간결성입니다. 람다식을 사용하면 익명 클래스(Anonymous Class)를 작성할 필요가 없으며, 반복적인 코드를 줄일 수 있습니다. 이로써 코드의 가독성이 향상되고 버그 발생 가능성이 감소합니다.

 

 

예를 들어, 리스트의 모든 요소를 출력하는 코드를 비교해 보겠습니다.

 

 

람다식을 사용하지 않을 때,

 

 

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
for (String name : names) {
    System.out.println(name);
}

 

 

 

람다식을 사용할 때,

 

 

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));

 

 

 

:: 람다식을 사용하면 반복문을 직접 작성할 필요가 없으므로 코드가 훨씬 간결해집니다.

 

 

 

 

확장성

 

람다식은 자바 코드를 더욱 확장 가능하게 만듭니다. 함수형 프로그래밍 개념을 도입함으로써, 코드를 모듈화 하고 함수를 인자로 전달하거나 반환하는 등의 작업을 보다 쉽게 수행할 수 있습니다. 이는 높은 추상화 수준의 코드를 작성하고 재사용성을 높일 수 있는 기회를 제공합니다.

 

 

예를 들어, 정렬 기준을 동적으로 변경해야 하는 경우 람다식을 사용하면 다양한 정렬 기준을 간단히 전달할 수 있습니다.

 

 

List<Person> people = // 리스트 초기화

// 이름을 기준으로 정렬
people.sort((p1, p2) -> p1.getName().compareTo(p2.getName()));

// 나이를 기준으로 정렬
people.sort((p1, p2) -> Integer.compare(p1.getAge(), p2.getAge()));

 

 

:: 람다식을 활용하면 코드의 확장성을 크게 향상시킬 수 있으며, 유연한 프로그래밍을 가능하게 합니다.

 

 

 

 


람다식의 기본 문법

 

함수형 인터페이스 (Functional Interface)

자바에서 람다식을 사용하려면 함수형 인터페이스(Functional Interface) 필요합니다. 함수형 인터페이스는하나의 추상 메서드만을 가진 인터페이스로, 람다식은 추상 메서드를 구현하는 코드를 제공합니다. 자바에서 기본적으로 제공하는 함수형 인터페이스 가지를 살펴보겠습니다.

 

 

 

자주 사용되는 함수형 인터페이스

 

 

1. Consumer <T>

: 값을 받아와서 소비하는 역할을 합니다.

 

Consumer<String> printer = (str) -> System.out.println(str);
printer.accept("Hello, Lambda!");

 

 

 

 

2. Supplier <T>

: 값을 제공하는 역할을 합니다.

 

Supplier<Integer> randomInteger = () -> (int) (Math.random() * 100);
int num = randomInteger.get();

 

 

 

 

3. Function <T, R>

: 값을 변환하는 함수를 표현합니다.

 

Function<Integer, String> intToString = (num) -> String.valueOf(num);
String strNum = intToString.apply(42);

 

 

 

 

4. Predicate <T>

: 조건을 검사하는 역할을 합니다.

 

Predicate<Integer> isEven = (num) -> num % 2 == 0;
boolean result = isEven.test(10);

 

 

 

:: 람다식은 이러한 함수형 인터페이스를 간단하게 구현할 수 있게 해 줍니다. 이러한 인터페이스는 자바 스트림(Stream) API와 같은 고수준 API에서 자주 사용됩니다.

 

 

 

 


람다식 작성 규칙

 

 

1. 파라미터 명시

람다식은 괄호 내에 파라미터를 명시합니다. 파라미터가 없는 경우 괄호 () 사용하고, 파라미터가 여러 개인 경우 괄호 안에 파라미터를 쉼표로 구분하여 나열합니다.

 

() -> System.out.println("Hello, Lambda!");
(str) -> System.out.println(str);
(x, y) -> x + y

 

 

 

2. 화살표(->) 사용

람다식의 파라미터와 본문을 구분하는데 화살표(->) 사용합니다.

 

(str) -> System.out.println(str)
(x, y) -> x + y

 

 

 

3. 중괄호 사용

람다식의 본문이 여러 문장으로 이루어져 있는 경우 중괄호 {} 로 감싸줍니다

 

(str) -> {
    System.out.println("Hello, ");
    System.out.println(str);
}

 

 

 줄짜리 람다식의 경우 아래와 같이 중괄호를 생략할  있습니다.

 

(x, y) -> x + y

 

 

 

4. 반환값 처리

람다식이 값을 반환하는 경우에는 return 키워드를 사용하지 않고 바로 반환 값을 작성합니다.

 

(x, y) -> x + y

 

 

 

5. 타입 유추

대부분의 경우, 컴파일러는 람다식의 파라미터 타입을 유추할 있으므로 파라미터의 타입을 생략할 있습니다

 

(str) -> System.out.println(str)

 

 

 

 하지만 명시적으로 타입을 지정할 수도 있습니다.

 

(String str) -> System.out.println(str)

 

 

 

 

 

예시 코드: 리스트 요소 출력

 

 

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

// 람다식을 사용하여 리스트 요소 출력
names.forEach(name -> System.out.println(name));

 

 

 

 

예시 코드: 숫자 제곱 계산

 

 

Function<Integer, Integer> square = x -> x * x;

int result = square.apply(5); // 결과: 25

 

 

 

 

예시 코드: 조건에 따른 필터링

 

 

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

// 짝수만 필터링
List<Integer> evenNumbers = numbers.stream()
        .filter(num -> num % 2 == 0)
        .collect(Collectors.toList());

 

 

 

:: 자바 람다식은 코드의 간결성과 확장성을 높여주는 강력한 기능입니다. 함수형 인터페이스를 이용하여 람다식을 작성하고, 규칙을 따르며 활용하면 더 효율적인 코드를 작성할 수 있습니다.

 

 

 

 


함수형 프로그래밍의 기본 개념

 

자바 함수형 프로그래밍은 불변성(Immutability), 일급 함수(First-Class Functions), 고차 함수(Higher-Order Functions) 같은 핵심 개념을 기반으로 합니다. 이러한 개념들을 자세히 알아보고, 자바에서의 함수형 프로그래밍에 어떻게 적용되는지 살펴보겠습니다.

 

 

불변성 (Immutability)

불변성은 데이터나 객체의 상태가 변하지 않는 성질을 의미합니다. , 생성된 데이터나 객체는 상태가 변경되지 않아야 합니다. 이러한 불변성은 함수형 프로그래밍에서 매우 중요한 개념 하나이며, 여러 가지 이점을 제공합니다.

 

불변성의 장점

1.  예측 가능한 코드
불변한 데이터나 객체는 예측 가능한 동작을 보장합니다. 상태가 변하지 않기 때문에 다른 부분에서 데이터에 대한 예상치 못한 변경을 방지합니다.

2. 병렬 처리 용이성
불변한 데이터는 여러 스레드 또는 프로세스에서 안전하게 공유할 수 있습니다. 데이터가 변경되지 않으므로 동기화 문제가 발생하지 않습니다.

3. 디버깅 및 테스트 용이성
불변한 코드는 디버깅과 테스트를 쉽게 만듭니다. 코드 일부가 변경되거나 부작용을 일으키는 경우를 추적하기 어렵기 때문에 더 안정적인 코드를 작성할 수 있습니다.

 

 

 

 


일급 함수 (First-Class Functions)

일급 함수란 함수를 다른 데이터 타입과 마찬가지로 다룰 있는 언어의 특징을 의미합니다. 자바는 이러한 개념을 완벽하게 지원하진 않지만, 함수형 프로그래밍을 위한 요소들을 지원함으로써 함수를 일급 시민으로 다룰 있게 해줍니다.

 

 

 

자바에서의 일급 함수

 

 

1. 함수를 변수에 할당

 

 : 함수를 변수에 할당하여 함수를 다룰  있습니다.

 

Function<Integer, Integer> square = x -> x * x;

 

 

 

2. 함수를 다른 함수의 인자로 전달

 

 : 함수를 다른 함수의 인자로 전달할 수 있습니다.

 

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.forEach(num -> System.out.println(square.apply(num)));

 

 

 

3. 함수를 다른 함수의 반환 값으로 사용

 : 함수를 다른 함수의 반환 값으로 사용할 수 있습니다.

 

Function<Integer, Function<Integer, Integer>> curriedAdd = x -> y -> x + y;

 

 

 

 


고차 함수 (Higher-Order Functions)

고차 함수는 함수를 인자로 받거나 함수를 반환하는 함수를 의미합니다. 고차 함수를 사용하면 코드를 더 모듈화 하고 재사용성을 높일 수 있습니다.

 

 

 

자바에서의 고차 함수 

 

자바에서는 고차 함수를 구현할 때 주로 인터페이스와 람다식을 활용합니다.

 

interface BinaryOperator<T> {
    T apply(T a, T b);
}

// 고차 함수
BinaryOperator<Function<Integer, Integer>> compose = (f, g) -> x -> f.apply(g.apply(x));

Function<Integer, Integer> addOne = x -> x + 1;
Function<Integer, Integer> square = x -> x * x;

Function<Integer, Integer> composedFunction = compose.apply(addOne, square);

int result = composedFunction.apply(2); // 결과: 9

 

 

:: 위 예제에서 compose 고차 함수로서, 개의 함수 addOne square 인자로 받아 새로운 함수를 반환합니다.

 

 

 

자바 함수형 프로그래밍의 핵심 개념인 불변성, 일급 함수, 고차 함수를 이해하면 코드를 더 모듈화하고 유연하게 작성할 수 있습니다.

 

 

 

 


람다식의 장점

 

 

1. 코드의 간결성

람다식은 함수형 프로그래밍의 핵심 개념 하나로, 메서드를 하나의 (expression)으로 표현한 것입니다. 람다식을 사용하면 코드를 간결하게 작성할 있으며, 익명 클래스(Anonymous Class) 사용하지 않고도 간단하고 명확한 코드를 작성할 있습니다.

 

코드의 간결성을 통한 이점

1) 반복 코드 제거
람다식을 사용하면 반복적인 코드를 크게 줄일 수 있습니다. 예를 들어, 리스트의 모든 요소를 출력하는 작업을 생각해보면 람다식을 사용하면 반복문을 직접 작성할 필요가 없으며 코드가 간결해집니다.

 

2) 가독성 향상

람다식을 사용하면 코드가 직관적이며 가독성이 향상됩니다. 람다식을 사용하면 메서드명과 중괄호를 생략할 있어 코드가 간결해집니다. 이는 코드 리뷰와 유지보수를 쉽게 만듭니다.

 

 

 

 

2. 병렬 프로그래밍의 편의성

병렬 프로그래밍은 멀티코어 프로세서를 활용하여 프로그램의 성능을 향상시키는 기술입니다. 자바에서는 스레드를 이용하여 병렬 프로그래밍을 구현할 수 있습니다.

 

 

람다식과 병렬 프로그래밍

 

람다식은 병렬 프로그래밍을 더욱 편리하게 만들어줍니다. 스레드를 사용하여 작업을 병렬로 처리할 때, 각 스레드에서 실행될 코드를 람다식으로 표현할 수 있습니다. 예를 들어, 리스트의 요소를 병렬로 처리하는 경우를 생각했을 때 람다식을 사용하면 작업을 분산하고 각 스레드에서 병렬로 실행할 코드를 명확하게 표현할 수 있습니다.

 

 

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

// 람다식을 사용하여 각 요소를 제곱하는 병렬 작업
numbers.parallelStream().forEach(num -> {
    int square = num * num;
    System.out.println(square);
});

 

 

:: 람다식을 활용하면 병렬 프로그래밍이 쉽고 안전하게 수행될 있습니다.

 

 

 

 

3. 함수형 스타일의 코드 작성

함수형 프로그래밍은 함수를 값처럼 다루고, 상태 변경을 최소화하며, 불변성을 유지하면서 프로그램을 작성하는 패러다임입니다. 람다식은 함수형 프로그래밍의 핵심 요소 중 하나이며, 자바에서도 이를 지원하고 있습니다.

 

 

함수형 스타일의 코드 작성

 

람다식을 사용하면 함수형 스타일로 코드를 작성할 수 있습니다. 이는 코드를 모듈화 하고 재사용성을 높이며, 프로그램의 동작을 예측 가능하게 만듭니다. 예를 들어, 리스트를 필터링하여 조건에 맞는 요소만 추출하는 함수형 스타일의 코드를 살펴봅시다.

 

 

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

List<Integer> evenNumbers = numbers.stream()
        .filter(num -> num % 2 == 0)
        .collect(Collectors.toList());

 

 

:: 이와 같이 람다식을 사용하면 함수형 프로그래밍의 개념을 자바 코드에 쉽게 적용할 수 있습니다.

 

 

 

 


람다식의 주의사항

자바 람다식은 코드를 간결하게 작성하고 함수형 프로그래밍 스타일을 도입할 수 있게 해 줍니다. 그러나 람다식을 사용할 때 주의해야 할 몇 가지 주요한 주의사항이 있습니다. 

 

 

변수 캡처 (Variable Capture)

람다식에서 외부 변수(클로저)를 사용하는 경우, 해당 변수의 값이 람다식에서 어떻게 캡처되는지 이해하는 것이 중요합니다. 람다식은 주변 범위의 변수를 캡처할 수 있으며, 이를 통해 외부 변수를 읽고 수정할 수 있습니다.

 

변수 캡처의 이점

변수 캡처는 람다식을 활용하는데 중요한 역할을 합니다. 외부 범위의 변수를 사용하면 람다식이 외부 범위와 상호작용할 수 있게 되며, 람다식은 주변 환경을 기억하고 사용할 수 있게 됩니다. 이는 유연하고 간결한 코드를 작성하는데 도움을 줍니다.

 

 

 

변수 캡처시 주의사항

 

 

1. 최종 변수 (Final Variable)

람다식에서 사용되는 외부 변수는 사실상 final 변수이어야 합니다. 이는 변수에 새로운 값을 할당할 없음을 의미합니다. 이러한 규칙을 준수하지 않으면 컴파일 에러가 발생합니다.

 

int x = 10;
Consumer<Integer> consumer = (value) -> {
    // x = 20; // 에러: 값을 재할당할 수 없음
    System.out.println(x + value);
};

 

 

2. 효과적으로 final인 변수

람다식 내에서 변수를 수정할 없지만, 변수가 효과적으로 final 경우에만 사용할 있습니다. , 변수는 final 키워드로 명시적으로 선언되지 않아도 람다식에서 수정되면 됩니다.

 

 

 

 


예외 처리

자바 람다식 내에서 예외를 처리하는 것은 중요한 주의사항 중 하나입니다. 람다식 내에서 예외가 발생하면 해당 예외를 처리하는 방법을 알고 있어야 합니다.

 

예외 처리의 이점 

람다식 내에서 명시적인 예외 처리를 통해 코드를 더욱 안정하게 만들 수 있습니다. 예외 처리를 통해 예외가 발생한 경우 더 적절한 조치를 취할 수 있으며, 프로그램의 안전성을 높일 수 있습니다.

 

 

 

예외 처리의 주의사항

 

 

1. 명시적인 예외 처리

람다식 내에서 예외가 발생하면 해당 예외를 명시적으로 처리해야 합니다. 이는 try-catch 블록을 사용하거나, 호출자에게 예외를 던지는 방식으로 처리됩니다.

 

Function<Integer, Integer> divideByZero = (x) -> {
    if (x == 0) {
        throw new ArithmeticException("Divide by zero");
    }
    return 10 / x;
};

 

 

2. 예외 처리를 호출자에게 위임

람다식 내에서 예외를 처리하는 것은 중요하지만, 어떤 예외를 어떻게 처리할지 결정하는 것은 호출자의 역할입니다. 람다식 내에서 예외를 처리할 , 호출자에게 어떤 예외를 던질 것인지 신중하게 결정해야 합니다.

 

 

 

 


메서드 레퍼런스 활용

메서드 레퍼런스(Method Reference)는 람다식을 더 간결하게 표현하는 방법 중 하나입니다. 메서드 레퍼런스를 사용하면 이미 정의된 메서드를 람다식으로 참조하여 코드를 간결하게 만들 수 있습니다.

 

메서드 레퍼런스의 이점

메서드 레퍼런스를 활용하면 코드가 더 간결해지고 가독성이 향상됩니다. 이미 정의된 메서드를 활용하여 중복 코드를 줄이고, 함수형 스타일로 코드를 작성하는데 도움을 줍니다.

 

 

 

메서드 레퍼런스의 주의사항

 

 

1. 메서드 시그니처

메서드 레퍼런스를 사용할 때는 메서드 시그니처가 일치해야 합니다. 이는 메서드의 이름, 매개변수 개수, 매개변수 타입이 일치해야 함을 의미합니다.

 

// 유효한 메서드 레퍼런스
Function<Integer, String> intToString = String::valueOf;

// 유효하지 않은 메서드 레퍼런스: 시그니처 불일치
Function<Integer, String> invalidReference = Integer::toString;

 

 

2. 정적 메서드 레퍼런스

정적 메서드 레퍼런스를 사용할 때는 메서드가 정적(static)으로 선언되어 있어야 합니다. 인스턴스 메서드 레퍼런스를 사용하려면 객체가 존재해야 하므로 주의가 필요합니다.

 

// 유효한 정적 메서드 레퍼런스
Function<Integer, String> intToString = String::valueOf;

// 유효하지 않은 정적 메서드 레퍼런스
Function<Integer, String> invalidReference = Math::abs;

 

 

 

:: 자바 람다식은 코드의 간결성과 가독성을 향상시키는 강력한 도구이지만, 변수 캡처, 예외 처리, 메서드 레퍼런스를 올바르게 활용해야 합니다. 이러한 주의사항을 잘 이해하고 적절하게 활용하면 람다식을 더 효과적으로 사용할 수 있습니다.

 

 

 

 


람다식과 컬렉션 프레임워크

 

스트림 API 소개

스트림 API는 데이터를 처리하고 연산하는 방법을 혁신적으로 바꾼 자바의 핵심 기능 중 하나입니다. 스트림은 데이터의 흐름을 나타내며, 데이터를 다루는 데 있어서 람다식을 적극 활용합니다. 스트림은 컬렉션, 배열, I/O 자원 등 다양한 데이터 소스를 처리할 수 있습니다.

 

 

스트림 API의 주요 특징


1) 선언적 프로그래밍
스트림 API는 어떻게 데이터를 처리할지 선언적으로 정의할 수 있습니다. 이는 코드의 가독성과 유지보수성을 향상시킵니다.

2) 병렬 처리 지원
스트림 API를 활용하면 데이터를 병렬로 처리할 수 있어 멀티코어 프로세서를 활용한 성능 향상이 가능합니다.

3) 재사용성
스트림 연산을 여러 번 조합하여 재사용할 수 있으므로 코드의 중복을 줄이고 모듈화 된 연산을 정의할 수 있습니다.

4) 내부 반복 (Internal Iteration)
스트림 API는 내부적으로 반복을 처리하므로 개발자는 데이터 처리에만 집중할 수 있습니다.

 

 

 

 


스트림 연산

스트림 API는 데이터 처리를 위한 다양한 연산을 제공합니다. 이러한 연산은 중간 연산과 최종 연산으로 나뉩니다. 중간 연산은 스트림을 반환하며, 최종 연산은 결과를 반환합니다.

 

 

1. 중간 연산

중간 연산은 스트림을 반환하며, 여러 중간 연산을 연결하여 스트림 처리 파이프라인을 구성할 수 있습니다.

 

filter(Predicate<T> predicate): 주어진 조건에 맞는 요소만을 필터링합니다.
map(Function<T, R> mapper): 각 요소를 변환하여 새로운 스트림을 생성합니다.
▷ flatMap(Function<T, Stream<R>> mapper): 각 요소를 스트림으로 변환하고, 평면화된 스트림을 반환합니다.
▷ distinct(): 중복 요소를 제거한 스트림을 반환합니다.
▷ sorted(): 요소를 정렬한 스트림을 반환합니다.

 

 

 

2. 최종 연산

최종 연산은 스트림 처리를 종결하고 결과를 반환합니다. 최종 연산을 호출해야만 스트림 처리가 시작됩니다.

 

▷ forEach(Consumer<T> action): 각 요소에 대해 주어진 동작을 수행합니다.
▷ count(): 스트림의 요소 개수를 반환합니다.
▷ collect(Collector<T, A, R> collector): 스트림을 컬렉션, 맵 또는 다른 결과 형태로 수집합니다.
▷ reduce(BinaryOperator<T> accumulator): 스트림의 요소를 하나로 줄여 하나의 결과를 반환합니다.
▷ min(Comparator<T> comparator): 스트림의 최솟값을 반환합니다.
▷ max(Comparator<T> comparator): 스트림의 최댓값을 반환합니다.
▷ anyMatch(Predicate<T> predicate): 주어진 조건과 일치하는 요소가 하나라도 있는지 확인합니다.

 

 

 

 

예제 코드: 리스트에서 짝수를 필터링하여 출력

 

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

numbers.stream()
    .filter(n -> n % 2 == 0)  // 중간 연산: 짝수 필터링
    .forEach(System.out::println);  // 최종 연산: 출력

 

 

 

 

예제 코드: 문자열 리스트에서 길이가 5 이상인 요소 수 세기

 

List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");

long count = words.stream()
    .filter(word -> word.length() >= 5)  // 중간 연산: 길이가 5 이상인 요소 필터링
    .count();  // 최종 연산: 요소 개수 세기

System.out.println("길이가 5 이상인 단어 수: " + count);

 

 

 

 

예제 코드: 리스트의 제곱 값 구하기

 

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

List<Integer> squares = numbers.stream()
    .map(n -> n * n)  // 중간 연산: 제곱 값으로 변환
    .collect(Collectors.toList());  // 최종 연산: 리스트로 수집

System.out.println("제곱 값 리스트: " + squares);

 

 

 

 

:: 자바 람다식과 스트림 API는 데이터 처리를 더욱 간결하고 효율적으로 만들어주는 강력한 도구입니다. 스트림을 사용하면 데이터를 선언적으로 처리할 수 있으며, 병렬 처리를 통해 성능을 향상시킬 수 있습니다.

 

 

 

 


자바 8 이후의 함수형 프로그래밍 관련 기능

자바 8 이후의 함수형 프로그래밍 관련 기능은 코드의 간결성과 가독성을 향상시키며, 비동기 및 병렬 프로그래밍을 지원합니다. Optional 클래스를 사용하여 안전한 null 처리, CompletableFuture를 통해 비동기 작업 처리, @FunctionalInterface 애노테이션을 활용하여 함수형 프로그래밍을 지원받을 수 있습니다.

 

Optional 클래스

Optional은 자바 8에서 도입된 클래스로, null 처리와 예외 처리를 간편하게 할 수 있는 기능을 제공합니다. 이는 NullPointerException을 방지하고 코드의 안정성을 높이는 데 도움을 줍니다.

 

 

 

▶ Optional 클래스의 주요 메서드

 

  • of(T value): 비어있지 않은 Optional 객체를 생성합니다.
  • ofNullable(T value): 값이 null인 경우 비어있는 Optional 객체를 생성합니다.
  • isPresent(): 값이 존재하는지 확인합니다.
  • ifPresent(Consumer<T> consumer): 값이 존재하는 경우 주어진 동작을 수행합니다.
  • orElse(T other): 값이 존재하지 않을 때 기본 값을 반환합니다.
  • orElseGet(Supplier<T> supplier): 값이 존재하지 않을 때 Supplier로부터 값을 가져옵니다.
  • orElseThrow(Supplier<X> exceptionSupplier): 값이 존재하지 않을 예외를 던집니다.

 

 

Optional 클래스 예제 코드

 

Optional<String> name = Optional.of("Alice");
String result = name.orElse("Unknown"); // "Alice"

 

 

 


CompletableFuture

CompletableFuture는 자바 8부터 비동기 및 병렬 프로그래밍을 지원하는 클래스입니다. 이는 비동기 작업을 수행하고 작업이 완료되었을 때 결과를 처리할 수 있도록 도와줍니다.

 

 

 

▶ CompletableFuture의 주요 메서드

 

  • supplyAsync(Supplier<U> supplier): 비동기로 결과를 생성합니다.
  • thenApply(Function<T, U> fn): 결과를 변환합니다.
  • thenCompose(Function<T, CompletionStage<U>> fn): 다른 CompletableFuture와 조합하여 비동기로 결과를 처리합니다.
  • thenCombine(CompletionStage<U> other, BiFunction<T, U, V> fn): 두 개의 CompletableFuture 결과를 조합합니다.
  • exceptionally(Function<Throwable, T> fn): 예외 처리를 수행합니다.
  • join(): 작업이 완료될 때까지 대기하고 결과를 반환합니다.

 

 

CompletableFuture 예제 코드

 

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 42);
CompletableFuture<Integer> result = future.thenApply(n -> n * 2);
Integer finalResult = result.join(); // 84

 

 

 


@FunctionalInterface 애노테이션

@FunctionalInterface 애노테이션은 함수형 인터페이스를 정의할 때 사용됩니다. 함수형 인터페이스는 하나의 추상 메서드를 가지며, 람다식을 사용하여 이를 구현할 수 있습니다.

 

@FunctionalInterface의 역할

컴파일러에게 함수형 인터페이스임을 명시적으로 알립니다. 함수형 인터페이스를 정확하게 정의하고 유지하도록 돕습니다. 람다식과 메서드 레퍼런스를 활용한 함수형 프로그래밍을 간편하게 만듭니다.

 

 

 

@FunctionalInterface 예제 코드

 

@FunctionalInterface
interface MyFunction {
    int apply(int x, int y);
}

MyFunction add = (x, y) -> x + y;
MyFunction multiply = (x, y) -> x * y;

 

 

 

 


이 글에서는 자바 람다식에 대해 간략하게 살펴보았습니다.

람다식은 코드의 간결성과 확장성을 높이는 동시에 함수형 프로그래밍의 개념을 자바에 도입함으로써 더욱 강력한 언어로 진화하게 되었습니다.

람다식은 코드 작성과 유지보수를 더 쉽게 만들어주며, 더욱 효율적인 프로그래밍을 가능하게 합니다.