본문 바로가기
JAVA

람다(lambda) 표현식

by 바디스 2023. 2. 9.

람다란?

람다 함수는 프로그래밍 언어에서 사용되는 개념으로 익명 함수(Anonymous functions)를 지칭하는 용어입니다. Java 8에 도입되었으며 기능 인터페이스를 정의하는 보다 간결하고 읽기 쉬운 방법을 제공합니다.
람다 표현식은 매서드로 전달할 수 있는 익명 함수를 단순화한 것이라고 할 수 있습니다.

람다의 특징

익명

  • 람다는 보통의 메서드와는 달리 이름이 없습니다.

함수

  • 람다는 메서드처럼 파라미터 리스트, 바디, 반환 형식, 가능한 예외 리스트를 포함하지만 클래스에 종속되지 않으므로 함수라고 부릅니다.

전달

  • 메서드 인수로 전달하거나 변수로 저장할수 있습니다.

간결성

  • 익명 클래스처럼 클래스 이름, 메서드 이름, 파라미터 타입, 반환 타입 등이 없기 때문에 코드가 간결합니다.

람다의 장점

  • 코드를 간결하게 만들 수 있다.
  • 식에 개발자의 의도가 명확히 드러나 가독성이 높아진다.
  • 함수를 만드는 과정없이 한번에 처리할 수 있어 생산성이 높아진다.
  • 병렬프로그래밍이 용이하다.

람다의 단점

  • 람다를 사용하면서 만든 무명함수는 재사용이 불가능하다.
  • 디버깅이 어렵다.
  • 람다를 남발하면 비슷한 함수가 중복 생성되어 코드가 지저분해질 수 있다.
  • 재귀로 만들경우에 부적합하다.

람다식 예제

// 기존의 방식
반환티입 메소드명 (매개변수, ...) {
    실행문
}

// 예시
public String hello() {
    return "Hello World!";
}
// 람다 방식
(매개변수, ... ) -> { 실행문 ... }

// 예시
() -> "Hello World!";

람다의 구조

람다 식의 구문은 다음 부분으로 구성됩니다.

  • 람다 파라미터 : 쉼표로 구분되고 괄호로 묶인 0개 이상의 인수 목록
  • 화살표 : ->
  • 람다 바디 : 값을 반환하거나 일부 작업을 수행하는 코드 블록스크린샷 2023-02-08 오후 4 53 42
//정상적인 유형
() -> {}
() -> 1
() -> { return 1; }

(int x) -> x+1
(x) -> x+1
x -> x+1
(int x) -> { return x+1; }
x -> { return x+1; }

(int x, int y) -> x+y
(x, y) -> x+y
(x, y) -> { return x+y; }

(String lam) -> lam.length()
lam -> lam.length()
(Thread lamT) -> { lamT.start(); }
lamT -> { lamT.start(); }


//잘못된 유형 선언된 type과 선언되지 않은 type을 같이 사용 할 수 없다.
(x, int y) -> x+y
(x, final y) -> x+y  

람다의 활용

함수형 인터페이스

람다 식의 주요 사용 사례 중 하나는 단일 추상 메서드가 있는 인터페이스인 기능적 인터페이스를 구현하는 것입니다.

@FunctionalInterface

Functional Interface는 일반적으로 '구현해야 할 추상 메소드가 하나만 정의된 인터페이스'를 가리킵니다.

//구현해야 할 메소드가 한개이므로 Functional Interface이다.
@FunctionalInterface
public interface Math {
    public int Calc(int first, int second);
}

//구현해야 할 메소드가 두개이므로 Functional Interface가 아니다. (오류 사항)
@FunctionalInterface
public interface Math {
    public int Calc(int first, int second);
    public int Calc2(int first, int second);
}

함수형 인터페이스 람다 사용예제

인터페이스 선언

@FunctionalInterface
interface Math {
    public int Calc(int first, int second);
}

추상 메소드 구현 및 함수형 인터페이스 사용

public static void main(String[] args){

   Math plusLambda = (first, second) -> first + second;
   System.out.println(plusLambda.Calc(4, 2));

   Math minusLambda = (first, second) -> first - second;
   System.out.println(minusLambda.Calc(4, 2));
}

실행 결과

6
2

이외에도 Java 8은 java.util.function 에서 인터페이스들을 지원하고 있습니다.

java.util.function패키지가 제공하는 FunctionalInterface정리

stream에서 람다 활용

java.util.stream 패키지에서 map, filter, reduce 및 flatMap과 같은 스트림에 대한 작업을 수행하는 컬렉션의 요소 반환에 람다가 활용됩니다.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squares = numbers.stream().map(x -> x * x).collect(Collectors.toList());
System.out.println(squares); // Outputs [1, 4, 9, 16, 25]
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evenNumbers = numbers.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
System.out.println(evenNumbers); // Outputs [2, 4]

메서드 참조

메서드 참조를 이용하면 기존의 메서드 정의를 재활용해서 람다처럼 전달할 수 있습니다. 때로는 람다 표현식보다 메서드 참조를 사용하는 것이 가독성이 좋을 수 있습니다.

inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));

위의 코드를 메서드 참조와 java.util.Comparator.comparing을 활용한다면 더 간단하게 코드 작성이 가능합니다.

inventory.sort(comparing(Apple::getWeight));

람다와 메서드 참조 단축 표현 예제

람다 메서드 참조 단축 표현
(Apple apple) -> apple.getWeight() Apple::getWeight
() -> Thread.currentThread().dumpstack() Thread::currentThread()::dumpStack
(str, i) -> str.substring() String::substring
(String s) -> System.out.println(s) System::printLn
(String s) -> this.isvalidName(s) this::isValidName

매서드 참조를 만드는 방법

메서드 참조는 세 가지 유형으로 구분할 수 있습니다.

  • 정적 메소드
    예를 들어 Integer의 parsInt 메서드는 Integer::parseInt로 표현할 수 있습니다.
  • 다양한 형식의 인스턴스 메서드 참조
    예를 들어 String의 length 메서드는 String::length로 표현할 수 있습니다.
  • 기존 객체의 인스턴스 메서드 참조
    예를들어 Transaction 객체를 할당받은 expensiveTransaction 지역 변수가 있고, Transaction 객체에는 getValue 메서드가 있다면, 이를 expensibeTransaction::getValue라고 표현할 수 있습니다.

생성자 참조

  • ClassName::new 생성자 참조를 만들 수 있습니다.
static Map<String, Function<Integer, Fruit>> map = new HashMap<>(); static { map.put("apple", Apple::new); map.put("orange", Orange::new); // 등등 }
public static Fruit giveMeFruit(String fruit, Integer weight) {  
return map.get(fruit.toLowerCase()) // map에서 Function<Integer, Fruit>를 얻었다.  
.apply(weight); // Function의 apply 메서드에 정수 무게 파라미터를 제공해서 Fruit를 만들 수 있다.  
}

람다 표현식을 조합할 수 있는 유용한 메서드

자바 8 API의 몇몇 함수형 인터페이슨느 다양한 유틸리티 매서드를 포함합니다.
Comparator, Function, Predicate 같은 함수형 인터페이스는 람다 표현식을 조합할 수 있도록 유틸리티 메서드를 제공합니다.

Comparator 조합

정적 매서드 Comparator.comparing을 이용해서 비교에 사용할 키를 추출하는 Function 기반의 Comparator을 반환할 수 있습니다.

Comparator c = Comparator.comparing(Apple::getWeight);

역정렬

inventory.sort(comparing(Apple::getWeight).reversed());

Comparator 연결

  • thenComparing 메서드로 두 번째 비교자를 만들 수 있다.
  • thenComparing은 (comparing 메서드처럼) 함수를 인수로 받아 첫 번째 비교자를 이용해서 두 객체가 같다고 판단되면 두 번째 비교자에 객체를 전달한다.
inventory.sort(comparing(Apple::getWeight).reversed().thenComparing(Apple::getCountry)); // 두 사과의 무게가 같으면 국가별로 정렬

Predicate 조합

Predicate 인터페이스는 복잡한 프레디케이트를 만들 수 있도록 negate, and, or 세 가지 메서드를 제공하고 있습니다.

Predicate notRedApple = redApple.negate(); // 기존 프레디케이트 객체 redApple의 결과를 반전시킨 객체를 만든다.  
Predicate redAndHeavyApple = redApple.and(apple -> apple.getWeight() > 150); // 두 프레디케이트를 연결해서 새로운 프레디케이트 객체를 만든다.  
Predicate redAndHeavyAppleOrGreen = redApple.and(apple -> apple.getWeight() > 150)  
.or(apple -> GREEN.equals(a.getColor())); // 프레디케이트 메서드를 연결해서 더 복잡한 프레디케이트 객체를 만든다.

Function 조합

Function 인터페이스는 인스턴스를 반환하는 andThen, compose 두 가지 디폴트 메서드를 제공합니다.

  • andThen 메서드는 주어진 함수를 먼저 적용한 결과를 다른 함수의 입력으로 전달하는 함수를 반환한다.
Function<Integer, Integer> f = x -> x + 1; Function<Integer, Integer> g = x -> x \* 2; Function<Integer, Integer> h = f.andThen(g); // 수학으로는 write g(f(x)) 또는 (g ∘ f)(x)라고 표현 int result = h.apply(1); // 4를 반환
  • compose 메서드는 인수로 주어진 함수를 먼저 실행한 다음에 그 결과를 외부 함수의 인수로 제공한다.
Function<Integer, Integer> f = x -> x + 1; Function<Integer, Integer> g = x -> x \* 2; Function<Integer, Integer> h = f.compose(g); // 수학으로는 f(g(x)) 또는 (f ∘ g)(x)라고 표현 int result = h.apply(1); // 3을 반환  

'JAVA' 카테고리의 다른 글

[JPA] Fetch와 Fetch Join  (0) 2023.09.21
java.util.function패키지가 제공하는 FunctionalInterface정리  (0) 2023.02.09

댓글