모던 자바 인 액션 정리본
람다 표현식
람다 표현식은 메서드로 전달할 수 있는 익명 함수를 단순화한 것이라고 할 수 있다.
람다의 특징
- 익명 : 보통의 메서드와 달리 이름이 없으므로 익명이라 표현한다.
- 함수 : 메서드처럼 특정 클래스에 종속되지 않으므로 함수라고 부른다.
- 전달 : 람다 표현식을 메서드 인수로 전달하거나 변수로 저장할 수 있다.
- 간결성 : 익명 클래스처럼 코드를 구현할 필요가 없다.
람다는 세 부분으로 이루어진다.
( Apple a1, Apple a2 ) -> a1.getWeight().compareTo(a2.getWeight());
람다 파라미터 화살표 람다 바디
- 파라미터 리스트 : Comparator의 compare 메서드 파라미터 ( 사과 두 개 )
- 화살표 : 화살표(->)는 람다의 파라미터 리스트와 바디를 구분
- 람다 바디 : 두 사과의 무게를 비교한다. 람다의 반환값에 해당하는 표현식
람다는 언제, 어떻게 사용하는 것인가?
함수형 인터페이스라는 문맥에서 람다 표현식을 사용할 수 있다.
- 함수형 인터페이스 : 정확히 하나의 추상 메서드를 지정하는 인터페이스 Ex) Comparator, Runnable ( default method가 있더라도 상관없다. )
- 함수 디스크립터(function descriptor) : 람다 표현식의 시그니처를 서술하는 메서드 Ex)Predicate, Consumer, Function, Supplier
람다 활용 : 실행 어라운드 패턴
람다를 적용하는 예제
기존 코드 : data.txt 파일에서 한 줄 읽어오는 코드 ( 예외처리까지 )
public String processFile() throws Exception{
try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))){
return br.readLine();
}
}
1단계. 동적 파라미터 적용 ( 이전 글 동적파라미터 참고 )
String result = processFile((BufferedReader br)->br.readLine() + br.readLine());
processFile 메서드를 사용하여 한 번에 두 줄 읽으려면 동적 파라미터르 적용하여 구할 수 있다.
이때 람다를 이용해서 동작을 전달할 수 있다.
2단계. 함수형 인터페이스를 이용해서 동작 전달
함수형 인터페이스 자리에 람다를 사용할 수 있다.
BufferedReader -> String과 IOEXception을 던질 수 있는 시그니처 함수형 인터페이스를 만들어야 한다.
@FunctionalInterface
public interface BufferedReaderProcessor {
String process(BufferedReader b) throws IOException;
}
정의한 인터페이스를 processFile 메서드의 인수로 전달할 수 있다.
public static String processFile(BufferedReaderProcessor p) throws IOException {
...
}
3단계. 동작실행
public static String processFile(BufferedReaderProcessor p) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(FILE))) {
return p.process(br);
}
}
4단계. 람다 전달
String oneLine = processFile((BufferedReader br) -> br.readLine());
String twoLine = processFile((BufferedReader br) -> br.readLine() + br.readLine());
oneLine : 한 행을 처리하는 코드
twoLine : 두 행을 처리하는 코드
함수형 인터페이스 사용
- Predicate
@FunctionalInterface
public interface Predicate<T> {
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)){
results.add(t);
}
}
return results;
}
Predicate<String> nonEmptyStringPredicate = (String s)-> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);
Predicate Interface는 test라는 추상 메서드를 정의하며 test는 제네릭 형식 T의 객체를 인수로 받아 boolean 반환한다.
그동안 인터페이스를 만들어 클래스를 따로 정의하는 작업을 거치지 않고 바로 사용할 수 있다는 점이 특징이다.
메서드 참조
기존의 메서드 정의를 재활용해서 람다처럼 전달할 수 있다 .
inventory.sort((Apple a1, Apple a2)->a1.getWeight().compareTo(a2.getWeight()));
inventory.sort(comparing(Apple::getWeight));
첫 번째 줄의 람다를 활용한 sort 를 메서드 참조와 java.util.Comparator.comparing을 활용하여
두 번째 줄의 코드를 완성하였다.
굳이 이렇게 까지 하는 이유는 뭘까? 가독성을 높일 수 있다는 점이다.
메서드 참조는 메서드명 앞에 구분자(::)를 붙이는 방식으로 메서드 참조를 활용할 수 있다.
위의 예시처럼 Apple::getWeight는 Apple 클래스에 정의된 getWeight의 메서드 참조이다.
메서드 참조에는 세가지 방법이 존재하는데,
첫 번째, 정적 메서드 참조
Integer::parseInt
두 번째, 다양한 형식의 인스턴스 메서드 참조
String::length
세 번째, 기존 객체의 인스턴스 메서드 참조
expensiveTransaction::getValue
람다 표현식에서 현존하는 외부 객체의 메서드를 호출할 때 사용된다.
지금까지 람다 표현식을 사용하는 이유, 방법에 대해 알아보았다.
요약하자면
- 람다 표현식은 익명 함수의 일종
- 함수형 인터페이스는 하나의 추상 메서드만을 정의하는 인터페이스
- 함수형 인터페이스를 기대하는 곳에서만 람다 표현식 사용가능
- 람다 표현식 전체가 함수형 인터페이스의 인스턴스로 취급
'자바' 카테고리의 다른 글
동시성 제어하기 (0) | 2023.10.05 |
---|---|
스트림 (0) | 2023.01.27 |
JVM이란 (0) | 2023.01.16 |
동적 파라미터화 (0) | 2023.01.10 |
Java List 와 배열 (1) | 2023.01.03 |