public static List<Apple> filterGreenApples(List<Apple> inventory) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if ("green".equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
After
선택 조건을 결정하는 인터페이스를 정의하자. (전략 디자인 패턴)
각 항목에 적용할 동작을 분리
public interface Predicate<T> {
boolean test(T t);
}
public class weightPredicate implements Predicate {
@Override
public boolean test(T t) {
return t.getWeight() > 150;
}
}
public class colorPredicate implements Predicate {
@Override
public boolean test(T t) {
return t.getColor() == Color.GREEN;
}
}
public <T> List<T> filter(List<T> list, Predicate<T> p) {
List<T> result = new ArrayList<>();
for (T e : list) {
if (p.test(e)) {
result.add(e);
}
}
return result;
}
//
List<Apple> greenApples = filter(inventory, new colorPredicate());
람다 표현식
메서드로 전달할 수 있는 익명 함수를 단순화한 것.
익명 함수의 일종이다 -> 이름은 없지만, 파라미터 리스트, 바디, 반환 형식을 가지고 예외를 던질 수 있다.
람다 표현식의 구성
파라미터 리스트
화살표
람다 바디
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
'----람다 파라미터---화살표-------------람다 바디------------------'
List<Apple> greenApples = filter(inventory, (Apple a) -> Color.GREEN.equals(a.getColor()));
함수형 인터페이스
함수형 인터페이스 : 오직 하나의 추상 메서드만을 정의하는 인터페이스
함수형 인터페이스를 기대하는 곳에서만 람다 표현식 사용 가능
제네릭 파라미터에는 참조형(Byte, Integer, Object, List..)만 사용 가능
Predicate
추상 메서드를 정의하여 제네릭 형식 T의 객체를 인수로 받아 Boolean 반환
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
public <T> List<T> filter(List<T> list, Predicate<T> p) {
List<T> result = new ArrayList<>();
for (T t : list) {
if (p.test(t)) {
result.add(t);
}
}
return result;
}
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfString, nonEmptyStringPredicate)
Consumer
추상 메서드를 정의하여 제네릭 형식 T의 객체를 인수로 받아 void 반환 (특정 동작 수행)
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
public <T> void forEach(List<T> list, Consumer<T> c) {
for(T t : list) {
c.accept(t);
}
}
forEach(
Arrays.asList(1,2,3,4,5),
(Integer i) -> System.out.println(i);
)
Function
추상 메서드를 정의하여 제네릭 형식 T의 객체를 인수로 받아 제네릭 형식 R 객체를 반환 (입력을 출력으로 매핑할 경우)
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
public <T, R> List<R> map(List<T> list, Function<T, R> f) {
List<R> result = new ArrayList<>();
for(T t : list) {
result.add(f.apply(t));
}
return result;
}
// [7, 2, 6]
List<Integer> l = map(
Arrays.asList("lambdas", "in", "action"),
(String s) -> s.length()
);
람다, 메서드 참조 활용
1단계: 코드 전달
객체 안에 동작을 포함시키는 방식으로 다양한 전략 전달
sort 동작은 파라미터화 되었다!
static class AppleComparator implements Comparator<Apple> {
@Override
public int compare(Apple a1, Apple a2) {
return a1.getWeight() - a2.getWeight();
}
}
inventory.sort(new AppleComparator());
2단계: 익명 클래스 사용
일회성이 있는 경우 익명 클래스를 이용하는 것이 좋다.
inventory.sort(new Comparator<Apple>() {
@Override
public int compare(Apple a1, Apple a2) {
return a1.getWeight() - a2.getWeight();
}
});
3단계: 람다 표현식 사용
함수형 인터페이스를 기대하는 곳 어디서나 람다 표현식을 사용할 수 있다.
// 1. Comparator 함수 디스크림터는 (T, T) -> int
inventory.sort((Apple a1, Apple a2) -> a1.getWeight() - a2.getWeight());
//2. 자바 컴파일러는 람다 표현식이 사용된 콘텍스트를 활용해서 람다의 파라미터 형식을 추론
inventory.sort((a1, a2) -> a1.getWeight() - a2.getWeight());
//3. comparing 메서드 사용
import static java.util.Comparator.comparing;
inventory.sort(comparing(apple -> apple.getWeight()));
4단계: 메서드 참조 사용
메서드 참조를 이용하면 람다 표현식의 인수를 더 깔끔하게 전달할 수 있다.
inventory.sort(comparing(Apple::getWeight));
람다 표현식 조합 유용 메서드
Comparator
정적 메서드 Comparator.comparing를 이용해서 비교에 사용할 키 추출
// 역정렬
inventory.sort(comparing(Apple::getWeight).reversed());
// Comparator 연결(thenComparing 메서드로 두 번째 비교자 만들기)
inventory.sort(comparing(Apple::getWeight)
.reversed()
.thenComparing(Apple::getContry));
Predicate
복잡한 Predicate를만들 수 있도록 negate, and, or 세 가지 메서드 제공
// 기존 Predicate 객체 결과를 반전시킨 객체 생성 (negate)
Predicate<Apple> notRedApple = redApple.negate();
// 두 Predicate 를 연결해 새로운 Predicate 객체 생성
//(and)
Predicate<Apple> redAndHeavyApple = redApple.and(apple -> apple.getWeight() > 150);
//(or)
Predicate<Apple> redAndHeavyOrGreen = redApple.and(apple -> apple.getWeight() > 150)
.or(apple -> GREEN.equals(a.getColor()));
Function
Function 인스턴스를 반환하는 andThen, compose 두 가지 default 메서드 제공
//andThen (주어진 함수 결과를 다른 함수의 입력으로 전달하는 함수 반환)
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g);
int result = h.apply(1); // g(f(x)) -> 4
//compose (인수로 주어진 함수를 먼저 실행한 후 그 결과를 외부 함수의 인수로 제공)
Function<Integer, Integer> h = f.compose(g);
int result = h.apply(1); // f(g(x)) -> 3