JAVA

JAVA8 | 람다 표현식

TECH 톡마스터 2023. 6. 15.

JAVA8 | 람다 표현식
JAVA8 람다 표현식

 안녕하세요! 오늘은 자바 8에서 추가된 강력한 문법인 람다 표현식에 대해 알아보려고 합니다. 람다 표현식은 기존의 자바 개발 방식을 혁신시키고, 코드를 더 간결하고 효율적으로 작성할 수 있는 방법을 제공합니다. 이 블로그에서는 람다 표현식의 개념을 알아보고, 실제로 어떻게 활용되는지 다양한 예제를 통해 살펴보겠습니다. 람다 표현식이 가져다주는 장점과 효과도 함께 알아보면서, 자바 8의 핵심 기능에 대해 자세히 알아보겠습니다. 그럼 함께 시작해 봅시다!

1. 람다 표현식의 개념

람다 표현식은 자바 8 버전에서 도입된 새로운 기능입니다. 람다 표현식은 익명 함수의 일종으로, 코드의 간결성과 가독성을 향상시키는 데 사용됩니다. 람다 표현식은 익명 함수이므로 이름이 없으며, 함수의 이름 대신 입력 매개변수와 반환 값만 지정합니다. 람다 표현식은 익명 함수이므로 함수 객체를 생성할 필요가 없으며, 함수 객체를 생성하는 데 필요한 코드가 생략됩니다. 따라서 람다 표현식을 사용하면 코드의 간결성과 가독성이 향상됩니다. 람다 표현식은 다음과 같은 경우에 사용됩니다.

  • 익명 함수를 생성해야 하는 경우
  • 익명 함수를 콜백 함수로 사용해야 하는 경우
  • 익명 함수를 스트림 API에서 사용해야 하는 경우

람다 표현식의 사용 예는 다음과 같습니다.

// 익명 함수를 생성합니다.
(x, y) -> x + y

// 익명 함수를 콜백 함수로 사용합니다.
list.sort((x, y) -> x.compareTo(y));

// 익명 함수를 스트림 API에서 사용합니다.
list.stream().filter((x) -> x > 10).forEach(System.out::println);

2. 함수형 인터페이스

 함수형 인터페이스(Functional Interface)는 람다 표현식을 지원하기 위해 자바 8에서 도입된 개념입니다. 함수형 인터페이스는 딱 하나의 추상 메서드만을 가지고 있어야 합니다. 이 추상 메서드는 람다 표현식으로 구현될 수 있으며, 함수형 인터페이스의 용도에 따라 다양한 동작을 수행할 수 있습니다.

2.1 함수형 인터페이스의 특징

  • 하나의 추상 메서드: 오직 하나의 추상 메서드만을 가져야 합니다.
  • 디폴트 메서드와 정적 메서드: 여러 개의 디폴트 메서드와 정적 메서드를 포함할 수 있습니다.
  • 어노테이션 @FunctionalInterface: 함수형 인터페이스임을 명시적으로 표시할 수 있습니다.

예제 코드를 통해 함수형 인터페이스를 이해해 보겠습니다.

@FunctionalInterface
interface Calculator {
    int calculate(int a, int b);
}

위의 예제는 Calculator라는 함수형 인터페이스를 정의한 것입니다. 이 인터페이스는 calculate라는 추상 메서드를 하나 가지고 있습니다.

Calculator addition = (a, b) -> a + b;
int result = addition.calculate(3, 5);
System.out.println("Addition: " + result); // Output: 8

 위의 코드에서는 Calculator 인터페이스를 활용하여 addition이라는 람다 표현식을 생성하고, 이를 이용해 두 정수의 합을 계산하는 예제입니다. addition 객체를 호출하면 내부적으로 calculate 메서드가 실행되어 덧셈 연산이 수행됩니다.

 함수형 인터페이스는 자바의 람다 표현식과 함께 사용되어 코드의 간결성과 가독성을 높여줍니다. 함수형 인터페이스를 정의하고, 람다 표현식으로 구현하여 다양한 동작을 간편하게 구현할 수 있습니다. 이를 통해 함수형 프로그래밍 스타일로 더욱 효율적이고 유연한 코드를 작성할 수 있습니다.

3. 람다 표현식의 문법

3.1 매개변수와 화살표

람다 표현식은 매개변수와 화살표( -> )를 사용하여 구성됩니다. 매개변수는 람다 표현식이 받아들일 입력값을 나타내며, 화살표는 매개변수와 람다 표현식의 몸체를 구분 짓는 역할을 합니다.

예제 코드를 통해 매개변수와 화살표를 이해해 보겠습니다.

Calculator addition = (a, b) -> a + b;

위의 코드에서 (a, b)는 매개변수를 나타내며,  ->는 화살표입니다. 이 코드는 두 개의 매개변수 a와 b를 받아들이는 람다 표현식을 생성하는 예제입니다.

3.2 중괄호와 반환문

람다 표현식의 몸체는 중괄호( {} )로 감싸진 코드 블록으로 구성됩니다. 코드 블록 내에서 필요한 동작을 구현하고, 결과를 반환할 수 있습니다. 예제 코드를 통해 중괄호와 반환문을 이해해 보겠습니다.

Calculator subtraction = (a, b) -> {
    int result = a - b;
    return result;
};

위의 코드에서 중괄호 안에는 뺄셈 연산을 수행하는 코드가 포함되어 있습니다. result 변수에 a - b의 결과를 저장하고, return 키워드를 사용하여 결과를 반환합니다.

3.3 타입 추론

람다 표현식은 자바 컴파일러가 컴파일 과정에서 타입을 추론할 수 있습니다. 따라서 매개변수의 타입을 명시하지 않아도 됩니다. 이를 "타입 추론"이라고 합니다.

예제 코드를 통해 타입 추론을 이해해 보겠습니다.

StringConverter converter = (str) -> Integer.parseInt(str);

위의 코드에서 str은 매개변수이며, 람다 표현식 내에서 문자열을 정수로 변환하는 코드가 포함되어 있습니다. 이때, str의 타입은 컴파일러가 자동으로 추론하게 됩니다.

 

람다 표현식의 문법을 적절히 활용하면 더욱 간결하고 가독성 있는 코드를 작성할 수 있습니다. 매개변수와 화살표로 매개변수를 구분하고, 중괄호와 반환문을 사용하여 람다 표현식의 몸 체를 구현하는 방식을 숙지하고, 타입 추론을 활용하여 타입 선언을 생략할 수 있는 점에 유의하면 됩니다.

 

4. 메서드 참조

 메서드 참조(Method Reference)는 람다 표현식을 더욱 간결하게 작성할 수 있는 기능입니다. 메서드 참조를 사용하면 이미 존재하는 메서드를 다른 코드에서 직접 참조하여 호출할 수 있습니다. 메서드 참조는 다음 세 가지 유형으로 나뉩니다.

4.1 정적 메서드 참조:

정적 메서드 참조는 람다 표현식이 정적 메서드를 호출하는 경우에 사용됩니다. 클래스명::정적메서드 형태로 작성합니다.

예제 코드를 통해 정적 메서드 참조를 이해해 보겠습니다.

Function<Integer, Integer> square = Math::square;
int result = square.apply(5);
System.out.println("Square: " + result); // Output: 25

위의 코드에서 Math::square는 Math 클래스의 square라는 정적 메서드를 참조하는 예제입니다. Function 함수형 인터페이스를 활용하여 입력값을 제곱하는 기능을 구현하였습니다.

4.2 인스턴스 메서드 참조

인스턴스 메서드 참조는 람다 표현식이 특정 객체의 인스턴스 메서드를 호출하는 경우에 사용됩니다. 객체참조::인스턴스메서드 형태로 작성합니다. 예제 코드를 통해 인스턴스 메서드 참조를 이해해 보겠습니다.

List<String> names = new ArrayList<>();
names.add("John");
names.add("Sarah");
names.add("Michael");
names.forEach(System.out::println);

위의 코드에서 System.out::println은 System.out 객체의 println이라는 인스턴스 메서드를 참조하는 예제입니다. forEach 메서드를 이용하여 리스트 내의 요소를 출력합니다.

4.3 생성자 참조

생성자 참조는 람다 표현식이 객체 생성을 위해 생성자를 호출하는 경우에 사용됩니다. 클래스명::new 형태로 작성합니다.

예제 코드를 통해 생성자 참조를 이해해 보겠습니다.

Supplier<List<String>> listSupplier = ArrayList::new;
List<String> names = listSupplier.get();
names.add("John");
names.add("Sarah");
names.add("Michael");

위의 코드에서 ArrayList::new는 ArrayList의 생성자를 참조하여 listSupplier가 새로운 ArrayList 객체를 생성하는 예제입니다. get 메서드를 호출하여 새로운 리스트를 얻을 수 있습니다.

 메서드 참조를 사용하면 코드를 더욱 간결하고 가독성 있게 작성할 수 있습니다. 이미 존재하는 메서드나 생성자를 직접 참조하여 재사용함으로써 코드의 중복을 줄이고 유지보수성을 높일 수 있습니다.

5. 람다 표현식의 활용 예제

 람다 표현식은 컬렉션 처리, 스트림 API 활용, 쓰레드와 병렬 프로그래밍 등 다양한 상황에서 유용하게 활용될 수 있습니다. 코드를 간결하게 작성하고 가독성을 높이는 동시에, 함수형 프로그래밍의 특징을 살려 유연하고 효율적인 프로그래밍을 할 수 있습니다.

5.1 컬렉션 처리

람다 표현식은 컬렉션 처리에 매우 유용하게 활용될 수 있습니다. forEach, filter, map 등의 메서드를 함께 사용하여 컬렉션의 요소를 처리하거나 변환할 수 있습니다. 예제 코드를 통해 컬렉션 처리를 이해해 보겠습니다.

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

// forEach를 사용하여 컬렉션의 요소 출력
numbers.forEach(number -> System.out.println(number));

// filter를 사용하여 짝수만 필터링
List<Integer> evenNumbers = numbers.stream()
                                   .filter(number -> number % 2 == 0)
                                   .collect(Collectors.toList());

// map을 사용하여 각 요소를 제곱하여 새로운 리스트 생성
List<Integer> squaredNumbers = numbers.stream()
                                      .map(number -> number * number)
                                      .collect(Collectors.toList());

위의 코드에서는 numbers라는 리스트를 생성하고, forEach를 사용하여 각 요소를 출력하는 예제입니다. 또한, filter를 사용하여 짝수만 필터링하여 evenNumbers 리스트를 생성하고, map을 사용하여 각 요소를 제곱하여 squaredNumbers 리스트를 생성합니다.

5.2 스트림 API 활용

람다 표현식은 스트림(Stream) API와 함께 사용되어 데이터의 처리와 변환을 효율적으로 수행할 수 있습니다. 스트림은 데이터의 연속된 흐름을 나타내며, 데이터를 처리하기 위한 다양한 연산을 제공합니다.

예제 코드를 통해 스트림 API 활용을 이해해 보겠습니다.

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

// filter와 map을 연결하여 짝수의 제곱합 계산
int sumOfSquaredEvenNumbers = numbers.stream()
                                     .filter(number -> number % 2 == 0)
                                     .map(number -> number * number)
                                     .reduce(0, Integer::sum);

// 스트림을 병렬 처리하여 연산 수행
int sum = numbers.parallelStream()
                 .reduce(0, Integer::sum);

위의 코드에서는 numbers라는 리스트를 생성하고, filter와 map을 연결하여 짝수의 제곱합을 계산하는 예제입니다. 또한, parallelStream을 사용하여 병렬 처리하여 연산을 수행하는 예제도 포함되어 있습니다.

5.3 쓰레드와 병렬 프로그래밍

람다 표현식은 쓰레드와 병렬 프로그래밍에서도 유용하게 활용될 수 있습니다. Runnable이나 Callable과 함께 사용하여 새로운 쓰레드를 생성하거나 병렬 처리를 수행할 수 있습니다.

예제 코드를 통해 쓰레드와 병렬 프로그래밍에서의 활용을 이해해 보겠습니다.

// Runnable을 사용한 쓰레드 생성
Thread thread = new Thread(() -> {
    for (int i = 0; i < 10; i++) {
        System.out.println("Thread: " + i);
    }
});
thread.start();

// Callable과 Future를 사용한 병렬 처리
ExecutorService executorService = Executors.newFixedThreadPool(5);
Future<Integer> future = executorService.submit(() -> {
    int sum = 0;
    for (int i = 1; i <= 100; i++) {
        sum += i;
    }
    return sum;
});
int result = future.get();
executorService.shutdown();

위의 코드에서는 Runnable을 사용하여 새로운 쓰레드를 생성하고 실행하는 예제입니다. 또한, Callable과 Future를 사용하여 병렬 처리를 수행하는 예제도 포함되어 있습니다. 쓰레드를 생성하거나 병렬 처리를 할 때 람다 표현식을 사용하면 코드의 간결성과 가독성을 높일 수 있습니다.

 

람다 표현식은 자바 8에서 도입된 강력한 문법으로, 코드를 간결하고 효율적으로 작성할 수 있게 해 줍니다. 예제를 통해 람다 표현식의 활용 방법을 자세히 살펴보았습니다. 이제 여러분도 람다 표현식을 활용하여 더욱 간결하고 효율적인 자바 코드를 작성해 보세요!

'JAVA' 카테고리의 다른 글

JAVA8 | 인터페이스 디폴트 메서드, Optional 클래스  (0) 2023.06.16
JAVA8 | 스트림 API  (0) 2023.06.16
JAVA | 15. 네트워킹  (0) 2023.06.12
JAVA | 14. GUI 프로그래밍  (0) 2023.06.12
JAVA | 13. 람다식과 스트림  (0) 2023.06.11

댓글

💲 추천 글