람다란 무엇인가?
- 메서드로 전달할 수 있는 익명 함수를 단순화한 것
- 구성
- 파라미터 리스트
- 바디
- 반환 형식
- 예외
기본적인 람다 표현식 ( 예제, 파라미터가 없는 형태 )
- () -> {}
- 파라미터가 없으며 void를 반환하는 람다 표현식
- 코드 예제
() -> { system.out.println("hello") }
- () -> “Raoul”
- 파라미터가 없으며 문자열을 반환하는 표현식
- () -> { return “Mario”; }
- 파라미터가 없으며 ( 명시적으로 return 문을 이용해서 문자열을 반환 )
함수형 인터페이스란?
- 정확히 하나의 추상메서드를 지정하는 인터페이스다.
- ex) Comparator, Runnable
- 디폴트메서드는 상관없다. ( 오직 필수로 구현해야할 추상메서드가 하나면 오케이 )
함수 디스크립터란?
- “람다 표현식의 시그니처를 서술하는 메서드”
- “함수형 인터페이스의 추상메서드 시그니처”
람다 사용법
- 함수형 인터페이스를 인수로 받는 메서드에만!! 람다표현식을 사용할 수 있다.
- 시그니처가 일치해야한다.
1
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로 선언해야한다.
형식 검사
- 람다가 사용된 콘텍스트를 확인
- 대상 형식 확인 ( 정의된 함수형 인터페이스 확인 )
=> 제네릭도 여기서 벗겨짐 - 해당 함수형 인터페이스의 추상형 메서드 확인
- 반환 타입, 인자 타입 확인
- 전달받은 시그니처와 요구되는 시그니처가 일치하는지 확인
- 같은 시그니처만 요구한다면 다른 함수형 인터페이스로도 전달가능
형식추론
- 자바컴파일러는 람다 표현식이 사용된 콘텍스트(대상 형식)을 이용해서 람다 표현식과 관련된 함수형 인터페이스를 추론한다.
- 대상형식을 이용해서 함수 디스크립터를 알수 있으며, 이를통해 람다의 시그니처도 추론할 수 있다.
형식추론은 무조건 하는게 좋다?
아니다. 명시적으로 형식을 포함하는 것이 가독성을 향상시킬때도 있고, 생략하는게 더 간결함을 제공해줄수도 있다.
람다속 변수 사용
변수
를 람다내부에서 사용한다면 해당 변수를자유변수
해당 행위를람다 캡처링
이라고 한다.- 람다는 인스턴스 변수와 정적 변수를 자유롭게 캡처(자신의 바디에서 참조)할 수 있다.
하지만 그러려면 지역변수는 명시적으로 final이 선언되어 있어야 하거나, 실질적으로 final로 선언된 변수와 똑같이 사용되어야한다. - 지역 변수 값은 스택에 존재하므로 자신을 정의한 스레드와 생존을 같이해야한다.
즉 복사값을 전달받으므로 final키워드가 붙어야한다.
왜? 이런 제약이 생긴걸까?
- 인스턴스 변수와 지역변수의 저장위치 차이 ( 인스턴스 => 힙메모리, 지역 => 스택 )
- 병렬 프로그래밍 때문에 ( 동시성 다루기 )
클로저
- 함수의 비지역 변수를 자유롭게 참조할 수 있는 함수의 인스턴스를 가르킨다
- 외부의 정의된 변수의 값에 접근하고, 값을 바꿀수 있다. “자바8의 람다와 익명클래스는 비슷한!! 역할을 한다”
=> 값을 바꿀수없다.
메서드 참조
- 명시적으로 메서드명을 참조하므로 가독성을 높일 수 있다.
메서드명 앞에 구분자(::)를 붙이는 방식으로 메서드참조를 활용할 수 있다.
1 2
(Apple a) -> a.getWeight() // 기존 람다식 Apple::getWeight // 메서드 참조를 활용한 람다식
- 컴파일러는 람다표현식의 형식을 검사하던 방식과 비슷한 과정으로 메서드 참조가 주어진 함수형 인터페이스와 호환하는지 확인한다.
=>메서드참조는 콘텍스트의 형식과 일치해야한다.
메서드 참조의 종류
- 정적 메소드 참조
1 2
(args) -> ClassName.staticMethod(args) // 기존 람다식 ClassName::staticMethod // 메서드 참조를 활용한 람다식
- 다양한 인스턴스 메서드 참조
1 2
(arg0, rest) -> arg0.instanceMethod(rest) // 기존 람다식 ClassName::instanceMethod // 메서드 참조를 활용한 람다식
- 기존 객체의 인스턴스 메서드 참조
1 2
(args) -> expr.instanceMethod(args) // 기존 람다식 expr::instanceMethod // 메서드 참조를 활용한 람다식
동작 파라미터화 만들기 정리
- 시그니처를 담은 인터페이스 생성후 구현
- 같은 시그니처의 익명클래스 생성
- 람다 표현식 사용
- static import를 통한 간결 람다표현
- 메서드참조