본문 바로가기

Java

Java 8 람다 표현식

1) 람다 표현식이란?

- 람다 표현식(이하 람다식)은 함수를 간단한 '식(expression)'으로 표현한 것을 의미합니다.   

  람다식을 작성하는 법은 반환 타입과 이름을 지우고,  ->를 블록 앞에 추가하면 됩니다.   

  람다식은 함수명을 가지지 않는데, 이러한 특징을 익명 함수(anonymous function)라고 합니다.

int max(int a, int b){
   return a > b ? a : b;     =>    (a, b) -> a > b ? a : b         
}

 

  

2) 람다식 작성 시 주의사항

- 람다식을 작성할 때는 몇 가지 주의사항이 있습니다. 

  (1) 매개변수가 하나인 경우에는, 괄호() 생략이 가능합니다. 

        단, 타입이 없을 때만 가능합니다.  

(a) -> a * a     =>   a -> a * a // OK
(int a) -> a * a  =>  int a -> a * a  // 에러

 (2) 블록 안의 문장이 하나일때는, 괄호{} 생략이 가능합니다. 

(int i) -> { System.out.println(i) }    =>   (int i) => System.out.println(i)

 

3) 람다식 예

Comparator<Apple> byWeight = new Comparator<Apple>(){
   public int compare(Apple a1, Apple a2){
       return a1.getWeight().compareTo(a2.getWeight());
   }
};

  위의 코드를 람다 표현식으로 나타내면 다음과 같이 나타낼 수 있습니다. 

Comparator<Apple> byWeight =
(Apple a2, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

  람다 표현식으로 나타냄으로 인해서, 코드가 훨씬 간결해지고 가독성이 좋아졌습니다. 

  람다 표현식의 구성 요소를 분해해서 설명하면 다음과 같습니다. 

(Apple a2, Apple a2)  ->  a1.getWeight().compareTo(a2.getWeight());
     람다 파라미터    화살표          람다 바디

람다 파라미터 - 람다 표현식의 메서드 파라미터입니다. 

화살표 - 람다의 파라미터 리스트와 바디를 구분합니다.

람다 바디 - 람다의 반환 값에 해당하는 표현식입니다. 

 

(1-1) 함수형 인터페이스

- 람다 표현식은 함수형 인터페이스를 선언해서 사용할 수도 있습니다.  

  함수형 인터페이스는 @FunctionalInterface라는 어노테이션을 사용하고,  

  하나의 추상 메서드만을 허용합니다. 

  

  다음은 함수형 인터페이스를 예시 코드로 살펴보겠습니다. 

@FunctionalInterface
public interface Calculate{
   public void operation(int a, int b);
}
public void calculateLambda(){

   Calculate calculateAdd = (a, b) -> a+b; 
   calculateAdd.operation(a,b);   
   
   Calculate calculateSubtract = (a,b) -> a-b;
   calculateSubtract.operation(a,b);
}

 

 

(1-2)  java.util.function 패키지

- Java 8에서는 java.util.function이라는 Functional 인터페이스를 위한 패키지를 제공합니다. 

  이 패키지가 제공하는 대표적인 Functional 인터페이스로는 Predicate, Consumer, Function 등이 있습니다. 

 

 (1) Predicate

- Predicate은 test라는 추상 메서드를 정의하고, test 메서드는 제너릭 형식의 T의 객체를 인수로 받아,

  이를 boolean 값으로 반환합니다. 

  Predicate의 예시 코드는 다음과 같습니다. 

@FunctionalInterface
public interface Predicate{
  boolean test(T t);
}


public <T> List<T> filter(List<T> list, Predicate<T> p){ 
    List<T> results = new ArrayList<>();
    for(T t: list){
      if(p.test(t){
         result.add(t);
      }
    }
    
    return result;
}


Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);

 

(2) Consumer

- Consumer 인터페이스는 제네릭 형식의 T 객체를 받아서, void를 반환하는 accept 추상 메서드를 정의합니다. 

  즉, T 형식의 객체를 받아서 어떤 동작을 수행하고 싶을 때, Consumer 인터페이스를 사용할 수 있습니다. 

  Consumer 인터페이스의 코드는 다음과 같습니다. 

@FunctionalInterface
public interface Consumer<T>{
  void accept(T t);
}

public <T> void forEach(List<T> list, Consumer<T> c){

     for(T t: list){
        c.accept(t);
     }
}

forEach(
   Arrays.asList(1,2,3,4,5),
   // Consumer의 accept 메서드를 구현하는 람다 표현식
   (Integer i) -> System.out.println(i);
);

 

(3) Function 인터페이스

- Function 인터페이스는 T 제네릭 객체를 받아서, R 제네릭 객체를 반환하는 apply 추상 메서드를 갖고 있습니다. 

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

public <T, R> List<R> map(List<T> list, Function<T, R> f){

      List<R> result = new ArrayList<>();

      for(T t: list){
        result.add(f.apply(t));
      }
}

List<Integer> l = map(
     Arrays.asList("lambdas", "in", "action");
     // Function 인터페이스의 apply 메서드를 구현하는 람다 표현식
     (String s) -> s.length();

 

- 2편에서는 Java 8 함수형 프로그래밍의 다른 주제인 스트림에 대해 다뤄보겠습니다. 

 

참고

자바의 신 2권

모던 자바 인 액션

Java 8 에서 왜 함수형 프로그래밍이 도입되었을까? (techcourse.co.kr)  

Functional Interface란 (techcourse.co.kr)  

'Java' 카테고리의 다른 글

자바 float과 double 자료형  (0) 2022.07.31
자바 int와 long 자료형  (0) 2022.07.31
Java HashMap 이해하기  (0) 2022.07.01
Object 클래스  (0) 2022.06.14
Collection 인터페이스  (0) 2022.05.30