람다란 ?

  • 익명 함수를 의미한다
  • 함수형 프로그래밍을 지원하기 위해 Java 8에서 도입되었다
  • 사용하면 코드가 간결해진다는 장점이 있다
  • 컬렉션과 Stream API를 사용할 때 매우 유용하다
  • 주로 [[함수형 인터페이스]]와 함께 사용한다

왜 람다를 사용하나?

  • 코드 간결성 및 가독성 향상
    • 특히 컬렉션 요소를 처리하는 데 유용하다
    • 익명 클래스나 반복적인 코드없이 간단하게 기능을 정의할 수 있다
  • 함수형 프로그래밍 지원
  • [[병렬 처리]] 및 스트림 API 강화
    • 람다 표현식은 스트림 API를 간결하게 작성할 수 있게 도와준다
  • 이벤트 처리 및 콜백 메커니즘 간소화
    • 비동기식 프로그래밍에서 콜백을 간단하게 정의할 수 있다

함수형 인터페이스

  • Consumer
    • java.util.function 패키지
    • void accpet(T t)를 가지고 있다
    • 주어진 입력 인수 t에 대한 작업을 수행한다
  • Function<T, R>
    • 입력을 하나 받아서 하나의 출력을 반환하는 인터페이스이다
    • R apply(T t)
      Fucntion<Integer, String> intToString = i -> "Number = " + i;
      System.out.println(intToString.apply(3));
      // 결과 Number = 5
  • Predicate
    • 입력을 하나 받아서 boolean을 반환하는 함수형 인터페이스이다
  • Supplier
    • 입력은 받지 않고, 값을 반환하는 인터페이스이다
  • BiConsumer
    • 두 개의 입력을 받아서 작업을 수행하고, 값을 반환하지 않는다
    • void accept(T t, U u)
  • BiFunction<T, U, R>
    • 두 개의 입력을 받아서 하나의 출력을 반환한다
    • R apply(T t, U u)
  • UnaryOperator
    • 하나의 입력을 받아서 동일한 타입의 결과를 반환한다
    • Function<T, T>의 특수화이다
    • T apply(T t)
  • BinaryOperator
    • 두 개의 동일한 타입을 입력 받아서 동일한 타입의 결과를 반환한다
    • BiFunction<T, T, T>의 특수화이다
    • T apply(T t1, T t2)

자바는 람다식을 어떻게 구현하고있나 ?

  • 내부적으로 invokeDynamic 바이트코드 명령어와 함께 사용된다
  • invokeDynamic은 런타임에 필요한 람다식을 동적으로 생성하고 호출한다
  • 컴파일 시, JVM은 람다식을 바이트코드로 변환한다
  • 이때, invokeDynamic 명령어가 사용되며, 런타임에 호출되는 메서드를 동적으로 연결한다
  • java.lang.invoke 패키지의 MethodHandle과 LambdaMetaFactory를 사용해서 런타임에 람다식을 생성한다
  • 이 과정에서 람다식은 실제로는 익명 클래스가 아닌, 바이트코드로 생성된다
  • JVM이 컴파일하면서 람다식을 만나면 invokeDynamic을 명령어를 사용한다
  • invokeDynamic이 붙은 코드는 런타임 환경에서 LambdaMetaFactory를 호출한다
  • LambdaMetaFactory는 CallSite를 생성하고 이를 통해 람다식을 호출한다
  • 람다식이 실제로 호출될 때 CallSite를 통해서 MethodHandle 사용된다
  • MethodHandle은 함수형 인터페이스가 가지고 있는 메서드를 참조하고 있다

Stream API란 ?

  • java 8에 추가된 기능으로 컬렉션이나 배열을 함수형 스타일로 처리할 수 있게 해준다
  • 기존 반복문을 사용하는 방식보다 코드가 간결하고 가독성이 높아지는 장점이 있다
  • 병렬 처리를 쉽게 구현할 수 있다

Stream API 특징

  • 중간 연산과 최정 연산
    • 중간 연산
      • 스트림을 반환하지만 실제 처리는 이루어지지 않는 연산
      • filter, map, sorted 등
    • 최종 연산
      • 스트림을 처리하여 결과를 생성하는 연산
      • collect, forEach, reduce
  • 지연 연산
    • 중간 연산은 지연 연산된다 그리고 최종 연산이 호출될 때 실제로 처리된다
    • 이를 통해서 성능 최적화를 할 수 있다