본문 바로가기
Study/모던 자바 인 액션

3장, 람다란 무엇인가?

반응형

람다란 무엇인가?

  • 메서드로 전달할 수 있는 익명 함수를 단순화한 것
  • 구성
    1. 파라미터 리스트
    2. 바디
    3. 반환 형식
    4. 예외

기본적인 람다 표현식 ( 예제, 파라미터가 없는 형태 )

  • () -> {}

    • 파라미터가 없으며 void를 반환하는 람다 표현식
    • 코드 예제

    () -> { system.out.println("hello") }

  • () -> “Raoul”
    • 파라미터가 없으며 문자열을 반환하는 표현식
  • () -> { return “Mario”; }
    • 파라미터가 없으며 ( 명시적으로 return 문을 이용해서 문자열을 반환 )

함수형 인터페이스란?

  • 정확히 하나의 추상메서드를 지정하는 인터페이스다.
  • ex) Comparator, Runnable
    • 디폴트메서드는 상관없다. ( 오직 필수로 구현해야할 추상메서드가 하나면 오케이 )

함수 디스크립터란?

  • “람다 표현식의 시그니처를 서술하는 메서드”
  • “함수형 인터페이스의 추상메서드 시그니처”

람다 사용법

  • 함수형 인터페이스를 인수로 받는 메서드에만!! 람다표현식을 사용할 수 있다.
  • 시그니처가 일치해야한다.
    Predicate<Apple> p = (Apple a) -> a.getWeight() > 150;

기본으로 제공되는 시그니처

  • Predicate

    (T) -> boolean

  • Consumer

    (T) -> void

  • Function<T, R>

    (T) -> R

  • Supplier

    () -> T

  • UnaryOperator

    (T) -> T



@FunctionalInterface

  • 해당 인터페이스가 함수형 인터페이스를 가르킴, 선언후 해당 인터페이스가 함수형 인터페이스의 규격을 따르지 않으면 컴파일러 오류를 발생시킨다.



실행 어라운드 패턴

  • 설정 -> 실행 -> 정리
  • 공통적인 설정과 정리 과정을 두고, 실제 자원 처리하는 코드를 입력받아 수행하는 작업
    • 설정과 정리과정을 공통함수로 빼서, 동작 파라미터화 시킨다.
    • 동작 파라미터화 시킨 시그니처와 동일한 함수형 인터페이스를 생성
    • 람다를 이용하여 “실행”과정만 전달하므로 실행 어라운드 패턴을 완성시킨다.



박싱 언박싱

  • 박싱한 값은 기본형을 감싸는 래퍼 클래스이며 힙에 저장된다.
  • 따라서 박싱한 값은 메모리를 더 소비하며, 메모리를 탐색하는 과정이 필요
    • 오토박싱을 피할 수 있는 특별한 버전의 함수형 인터페이스를 제공한다.
    • IntStream, LongStream ....



예외처리

  • 기존에 생성되어있는 함수형 인터페이스는 확인된 예외를 던지는 동작을 허용하지 않는다.
  • 확인된 예외를 선언하는 함수형 인터페이스를 직접 정의하거나, try-catch로 선언해야한다.



형식 검사

  1. 람다가 사용된 콘텍스트를 확인
  2. 대상 형식 확인 ( 정의된 함수형 인터페이스 확인 )

    => 제네릭도 여기서 벗겨짐
  3. 해당 함수형 인터페이스의 추상형 메서드 확인
  4. 반환 타입, 인자 타입 확인
  5. 전달받은 시그니처와 요구되는 시그니처가 일치하는지 확인
    • 같은 시그니처만 요구한다면 다른 함수형 인터페이스로도 전달가능

형식추론

  • 자바컴파일러는 람다 표현식이 사용된 콘텍스트(대상 형식)을 이용해서 람다 표현식과 관련된 함수형 인터페이스를 추론한다.

  • 대상형식을 이용해서 함수 디스크립터를 알수 있으며, 이를통해 람다의 시그니처도 추론할 수 있다.

  • 형식추론은 무조건 하는게 좋다?

    • 아니다. 명시적으로 형식을 포함하는 것이 가독성을 향상시킬때도 있고, 생략하는게 더 간결함을 제공해줄수도 있다.



람다속 변수 사용

  • 변수를 람다내부에서 사용한다면 해당 변수를 자유변수 해당 행위를 람다 캡처링이라고 한다.
  • 람다는 인스턴스 변수와 정적 변수를 자유롭게 캡처(자신의 바디에서 참조)할 수 있다.

    하지만 그러려면 지역변수는 명시적으로 final이 선언되어 있어야 하거나, 실질적으로 final로 선언된 변수와 똑같이 사용되어야한다.
  • 지역 변수 값은 스택에 존재하므로 자신을 정의한 스레드와 생존을 같이해야한다.

    즉 복사값을 전달받으므로 final키워드가 붙어야한다.

왜? 이런 제약이 생긴걸까?

  • 인스턴스 변수와 지역변수의 저장위치 차이 ( 인스턴스 => 힙메모리, 지역 => 스택 )
  • 병렬 프로그래밍 때문에 ( 동시성 다루기 )



클로저

  • 함수의 비지역 변수를 자유롭게 참조할 수 있는 함수의 인스턴스를 가르킨다
  • 외부의 정의된 변수의 값에 접근하고, 값을 바꿀수 있다. “자바8의 람다와 익명클래스는 비슷한!! 역할을 한다”

    => 값을 바꿀수없다.



메서드 참조

  • 명시적으로 메서드명을 참조하므로 가독성을 높일 수 있다.
  • 메서드명 앞에 구분자(::)를 붙이는 방식으로 메서드참조를 활용할 수 있다.
    (Apple a) -> a.getWeight() // 기존 람다식
    Apple::getWeight // 메서드 참조를 활용한 람다식
  • 컴파일러는 람다표현식의 형식을 검사하던 방식과 비슷한 과정으로 메서드 참조가 주어진 함수형 인터페이스와 호환하는지 확인한다.

    => 메서드참조는 콘텍스트의 형식과 일치해야한다.

메서드 참조의 종류

  • 정적 메소드 참조

    (args) -> ClassName.staticMethod(args) // 기존 람다식
    ClassName::staticMethod // 메서드 참조를 활용한 람다식
  • 다양한 인스턴스 메서드 참조

    (arg0, rest) -> arg0.instanceMethod(rest) // 기존 람다식
    ClassName::instanceMethod // 메서드 참조를 활용한 람다식
  • 기존 객체의 인스턴스 메서드 참조

    (args) -> expr.instanceMethod(args) // 기존 람다식
    expr::instanceMethod // 메서드 참조를 활용한 람다식



동작 파라미터화 만들기 정리

  1. 시그니처를 담은 인터페이스 생성후 구현
  2. 같은 시그니처의 익명클래스 생성
  3. 람다 표현식 사용
  4. static import를 통한 간결 람다표현
  5. 메서드참조
728x90
반응형