Modern Java in Action - Ch.3

참고: 책 - Modern Java in Action

Ch 3. 람다 표현식

람다의 특징

람다 문법 - 예제를 통해 알아보기

() -> {} //(O) 파라미터 X, void 반환
() -> "Poogle" // (O) 파라미터 X, 문자열 반환
() -> {return "Poogle"} // (O) 파라미터 X 명시적으로 문자열 반환
(Integer i) -> return "Poogle" + i; // (X)
(Integer i) -> {return "Poogle" + i;} // (O)
(String s) -> {"Iron Man";} //(X)
(String s) -> {return "Iron Man";} //(O)
(String s) -> "Iron Man" //(O)
process(() -> System.out.println("This is Awsome")); //(O)

execute(() -> {});
public void execute(Runnable r) {
    r.run();
} //(O)

public Callable<String> fetch() {
    return () -> "Tricky ;-)";
} //(O)

Predicate<Apple> p = (Apple a) -> get.weight(); //(X)

람다 활용 - 실행 어라운드 패턴

public static String processFileBefore() throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
        return br.readLine();
    }
}
import java.io.BufferedReader;
import java.io.IOException;

@FunctionalInterface
public interface BufferedReaderProcessor {
    String process(BufferedReader br) throws IOException;
}
public static String processFile(BufferedReaderProcessor p) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
        return p.process(br);
    }
}
public static void main(String[] args) throws IOException {
    String oneLine = processFile(BufferedReader::readLine);
    String twoLines = processFile((BufferedReader br) -> br.readLine() + br.readLine());
}



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;
}
List<String> listOfStrings = new ArrayList<>();
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);

Consumer

@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

@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;
}
List<Integer> l = map(
        Arrays.asList("lambdas", "in", "action"),
        (String s) -> s.length()
);



형식 검사, 형식 추론, 제약

지역 변수 사용

제약 이유는?



메서드 참조

스크린샷, 2021-08-23 02-50-07

//람다
ToIntFunction<String> stringToInt = (String s) -> Integer.parseInt(s);

//메서드 참조
Function<String, Integer> stringToInteger = Integer::parseInt;

다양한 형식의 인스턴스 메서드 참조

스크린샷, 2021-08-23 02-50-49

//람다
BiPredicate<List<String>, String> contains = (list, element) -> list.contains(element);

//메서드 참조
BiPredicate<List<String>, String> contains = List::contains;

기존 객체의 인스턴스 메서드 참조

스크린샷, 2021-08-23 02-50-49

//람다
Predicate<String> startsWithNumebr = (String string) -> this.startsWithNumber(string);

//메서드 참조
Predicate<String> startsWithNumber = this::startsWithNumber;

생성자 참조



람다 표현식의 조합

inventory.sort(comparing(Apple::getWeight)
         .reversed()
         .thenComparing(Apple::getCountry));

Predicate 조합

ex. 빨간색이 아닌 사과

Predicate<Apple> notRedApple = redApple().negate;

ex. 빨간색이면서 무거운 사과

Predicate<Apple> redAndHeavyApple = redApple.and(apple -> apple.getWeight() > 150);

ex. 빨간색이면서 무거운 사과 또는 그냥 녹색 사과

Predicate<Apple> redAndHeavyAppleOrGreen = redApple.and(apple -> apple.getWeight() > 150)
                                                   .or(apple -> GREEN.equals(a.getColor()));