[Java] lambda (2/2) - 람다 활용하기
![[Java] lambda (2/2) - 람다 활용하기](https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/mNa_V4J-GGY/upload/a7a28a67776e2b2149584684a97db68c.jpeg)
backend developer interested in technical problem
람다식 활용하기
람다를 활용할 때에는 동작 파라미터화, 익명 클래스, 람다 표현식, 메서드 참조등을 사용한다.
1단계 : 코드전달
자바 8의 List API에서 sort메서드를 제공해서 정렬 메서드를 직접 구현할 필요는 없다. 하지만 sort의 정렬 방식을 바꾼다면 어떻게 정렬 방식을 전달해야 할까
🎓 void sort (Comprator<? super E>)
(참고) 자바 공식 API문서의 Comparator설명.

API문서에서 Functional Interface. 함수형 인터페이스라고 해준다.

compare를 제외한 메서드는 모두 static으로 되어있다. 고로 구현해야할 메서드는 하나인 함수형 인터페이스이다.
sort는 Comparator객체를 인수로 받아 비교한다. 객체 안에 동작을 포함시키는 방식이니 sort의 동작은 파라미터화 되었다고 할 수 있다. 즉 sort에 전달된 정렬 전략에 따라서 sort의 동작이 달라진다.
public class AppleCompartor implements Comparator<Apple>{
@Override
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new AppleComparator());
2단계 : 익명 클래스 사용
한 번만 사용할 Comparator를 1단계 처럼 구현하는 것보다는 익명 클래스를 이용하는게 좋다.
inventory.sort(new Comparator<Apple>(){
public int compare(Apple a1, Apple 2,){
return a1,getWeight().compareTo(a2.getWeight());
}
})
3단계 : 람다 표현식 사용
우리는 함수형 인터페이스가 오는 곳 어디에서나 람다표현식을 사용할 수 있음을 알았다. Comparator의 함수는 (T, T) → int 의 형식이다.
(사과를 예시로 사용하고 있으니 정확히는 (Apple, Apple) → int)
그렇다면 코드를 아래처럼 개선할 수 있다.
//3단계 람다표현식 사용
inventory.sort((Apple a1, Apple a2)
-> a1.getWeight().compareTo(a2.getWeight()));
//3.5단계 파라미터 형식 생략하기
inventory.sort((a1, a2)
-> a1.getWeight().compareTo(a2.getWeight()));
또한 자바 컴파일러는 람다표현식을 통해서 람다의 파라미터 형식을 추론한다.
여기서 더 짧게 할 수 있다. Compator는 Comparable키를 추출해서 Comparator 객체로 만드는 Function 함수를 인수로 받는 정적메서드 comparing을 포함한다.

//comparing 사용
import static java.util.Comparator.comparing;
Comparator<Apple> c
= Comparator.comparing((Apple a) -> a.getWeight());
4단계 : 메서드 참조 사용
메서드 참조까지 사용하면 간단한 코드가 된다.
inventory.sort(comparing(Apple::getWeight));
또한 코드 자체도 명확해 졌다. Apple을 비교하고 weight별로 비교해서 inventory를 sort하라 는 의미가 전달된다.
람다식 조합하기
예시로 Comparator, Predicate, Function을 연결하는 예시를 들을 것이다.
Comparator
inventory.sort(comparing(Apple::getWeight))
.reversed()
.thenComparing(Apple::getCountry));
.정렬을 할 때 정렬 차순을 바꾸고싶다면? 이후 결과가 같으면 다른 기준을 제시하고 싶다면? Comparator를 연결해서 문제를 해결할 수있따.
(Comparator는 thenComparing메서드로 두 번째 비교자를 만들 수 있다.)
Predicate

Predicate 인터페이스는 복잡한 프레디케이트를 만들 수 있도록 negate, and ,or 세가지 메서드를 제공한다.
예를 들어 ‘빨간색이 아닌 사과’ 처럼 특정 프레디 케이트를 반전시킬 때에는 negate메서드를 사용할 수 있다.
negate
Predicate<Apple> notRedApple =
redApple.negate();
//기존 프레디케이트 객체 redApple의 결과를 반전시킨 객체를 만든다.
//Predicate는 boolean을 반환하니 boolean에 !를 붙인 셈
and
빨간색이면서 무거운사과를 선택하려면?
Predicate<Apple> redAndHeavyApple =
redApple.and(apple -> apple.getWeight() > 150);
or
그 뿐만 아니라 or를 이용해서 ‘빨간색이면서 무거운(150그램이상)사과 또는 그냥 녹색 사과` 라는 조건도 만들 수 있다.
Predicate<Apple> redAndHeavyAppleOrGreen =
redApple.and(apple -> apple.getWeight() > 150)
.or(apple -> GREEN.equals(a.getColor()));
Function

Function인터페이스는 Function 인스턴스를 반환하는 andThen, compose두 가지 디폴트 메서드를 제공한다.
andThen
andThen메서드는 주어진 함수를 먼저 적용한 결과를 다른 함수의 입력으로 전달하는 함수를 반환한다.
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g); //수학적으로는g(f(x))
int result h.apply(1); //(1+1) * 2 = 4반환
compose
compose는 인수로 주어진 함수를 먼저 실행한 다음에 그 결과를 외부 함수의 인수로 제공한다.
즉. f.andThen(g)에서 andThen대신에 compose를 사용하면 g(f(x))가 아니라 f(g(x))가 된다.
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.compose(g); //수학적으로는f(g(x))
int result h.apply(1); //(1*2) + 1 = 3반환




