람다 표현식 은 메서드로 전달할 수 있는 익명 함수를 단순화한 것으로 람다의 특징은 다음과 같다.
- 익명 : 보통 메서드와 달리 이름이 없다.
- 함수 : 특정클래스의 종속되지 않는다.
- 전달 : 인수로 전달하거나 변수로 저장할 수 있다.
- 간결성 : 익명 클래스처럼 자질구레한 코드 구현이 필요없다.
람다는 세부분으로 이루어 진다.1
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
- 람다 파라미터 : (Apple a1, Apple a2)
- 화살표 : ->
- 람다 바디 : a1.getWeight().compareTo(a2.getWeight())
람다의 기본 문법
다음은 람다의 기본 문법이다.
1 | (parameters) -> expression |
람다 사용법
람다 표현식은 함수형 인터페이스 라는 문맥에서 사용할 수 있다.
함수형 인터페이스
함수형 인터페이스 는 정확히 하나의 추상 메서드를 지정하는 인터페이스다.
우리가 잘 아는 자바 API의 함수형 인터페이스는 Comparator, Runnable 등이 있다.1
2
3
4
5
6
7public interface Comparator<T> {
int compare(T o1, T o2);
}
public interface Runnable {
void run();
}
함수 디스크립터
함수 디스크립터 는 람다표현식의 시그니처를 의미한다. 다른말로 함수형 인터페이스의 추상 메서드 시그니처와 같다.
예) Predicate
람다 활용: 실행 어라운드 패턴
실행 어라운드 패턴은 실제 자원을 처리하는 코드를 설정과 정리 두 과정이 둘러싸는 형태를 갖는다.
파일 읽는 예제를 통해, 변환과정을 알아보자.1
2
3
4
5public static String processFile() throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
return br.readLine(); // 실제 필요한 작업을 하는 행
}
}
1단계 : 동작 파라미터화
실제 필요한 작업 내용이 달라질 수 있기 때문에 processFile의 동작을 파라미터화 할 필요가 있다.
1 | String result = processFile((BufferedReader br) -> br.readLine() + br.readLine()); |
2단계 : 함수형 인터페이스를 이용해 동작 전달
1 |
|
3단계 : 동작 실행
1 | public static String processFile(BufferedReaderProcessor p) throws IOException { |
4단계 : 람다 전달
이제 람다를 이용해 다양한 동작을 processFile 메서드로 전달할 수 있다.
1 | String oneLine = processFile((BufferedReader br) -> br.readLine()); |
함수형 인터페이스 사용
자바 8 라이브러리 설계자들은 java.util.function 패키지로 여러가지 새로운 함수형 인터페이스를 제공한다.
Predicate
Predicate1
2
3
4
public interface Predicate<T> {
boolean test(T t);
}
##
Consumer
Consumer
1 |
|
Function
Function<T,R> 인터페이스는 apply 추상 메서드를 정의하고, apply는 제네릭 형식 T를 인수로 받아 제네릭 형식 R 객체를 반환한다.1
2
3
4
public interface Function<T, R> {
R apply(T t);
}
기본형 특화
제네릭 파라미터에는 참조형(reference type)만 사용가능하여 박싱/언박싱 작업이 필요하고, 이러한 변환 과정은 비용소모가 된다. 불필요한 비용소모를 방지하기 위해 기본형특화 버전의 함수형 이넡페이스를 제공한다.
예) IntPredicate, LongPredicate, DoublePredicate..
대표적 함수형 인터페이스
함수형 인터페이스 | 함수 디스크립터 |
---|---|
Predicate |
T -> boolean |
Consumer |
T -> void |
Function<T, R> | T -> R |
Supplier |
() -> T |
BinaryOperator |
(T, T) -> T |
BiPredicate<L, R> | (L, R) -> boolean |
BiConsumer<T, U> | (T, U) -> void |
BiFunctional<T, U, R> | (T, U) -> R |
형식검사, 형식 추론, 제약
같은 람다, 다른 함수형 인터페이스
대상형식 이란, 어떤 콘텍스트에서 기대되는 람다 표현식의 형식을 일컫는다.
대상 형식이라는 특징 때문에 같은 람다 표현식이더라도 호환되는 추상 메서드를 가진 다른 함수형 인터페이스로 사용될 수 있다.
형식 추론
자바 컴파일러는 람다 표현식이 사용된 콘텍스트(대상 형식)를 이용해서 람다 표현식과 관련된 함수형 인터페이스를 추론한다. 컴파일러는 람다의 시그니처도 추론할 수 있고, 람다표현식의 파라미터 형식에 접근할 수 있다.
자바 컴파일러는 다음처럼 람다 파라미터 형식을 추론할 수 있다.1
List<Apple> greenApples = filter(inventory, a -> "green".equals(a.getColor())); // 파라미터 a에는 타입을 명시적으로 지정하지 않았다.
지역변수 사용
람다에서는 외부변수를 사용할 수 있으며 이와 같은동작을 람다 캡처링 이라고 부른다.1
2int portNumber = 3389;
Runnable r = () -> System.out.println(portNumber);
제약이 있다면, portNumber를 final로 선언하거나 final이 선언된 변수와 같이 사용(초기화 후 변경x)되어야 한다.
메서드 레퍼런스
메서드 레퍼런스 는 특정 메서드만을 호출하는 람다의 축양형이라 생각할 수 있다.
메서드 레퍼런스는 세가지 유형으로 구분 할 수 있으며, 아래와 같다.
- 정적 메서드 레퍼런스
- 다양한 형식의 인스턴스 메서드 레퍼런스
- 기존 객체의 인스턴스 메서드 레퍼런스
생성자 레퍼런스
ClassName::new 처럼 클래스명과 new 키워드를 이용해서 기존 생성자의 레퍼런스를 만들 수 있다.
1 | // 생성자 레퍼런스 방식 |
Reference : Java 8 in Action