[Java8] 람다 표현식

람다 표현식

목차

람다란 무엇인가?

람다 표현식-설명

어디에, 어떻게 람다를 사용할까?

함수형 인터페이스

함수 디스크립터

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

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

public static String processFileLimited() throws IOException {
    try (BufferedReader br =
                 new BufferedReader(new FileReader("lambdasinaction/chap3/data.txt"))) {
        return br.readLine();
    }
}

1단계: 동작 파라미터화를 기억하라

-processFile 메서드만 다른 동작을 수행하도록 할 때 processFile의 동작을 파라미터화하는 것이다.

String twoLines = processFile((BufferedReader b) -> b.readLine() + b.readLine());

2단계: 함수형 인터페이스를 이용해서 동작 전달

@Functionallnterface
public interface BufferedReaderProcessor{
    public String process(BufferedReader b) throws IOException;
}

public static String processFile(BufferedReaderProcessor p) throws IOException {
	...
}

3단계: 동작 실행!

public static String processFile(BufferedReaderProcessor p) throws IOException {
    try(BufferedReader br = new BufferedReader(new FileReader("lambdasinaction/chap3/data.txt"))){
        return p.process(br);
    }
}

4단계:람다 전달

String oneLine = processFile((BufferedReader b) -> b.readLine());

String twoLines = processFile((BufferedReader b) -> b.readLine() + b.readLine());

함수형 인터페이스 사용

Predicate

@Functionallnterface
public interface Predicate<T> {
  boolean test(T t);
}

public static <T> List<T> filter(List<T> list, Predicate<T> p) {
  List<T> results = new ArrayList<>();
  for(T s: list) {
    if(p.test(s)) {
      results .add(s);
    }
  }
  return results;
}
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);

Consumer

@Functionallnterface
public interface Consumer<T> {
  void accept(T t);
}

public static <T> void forEach(List<T> list, Consumer<T> c) {
  for(T i: list) {
    c.accept(i);
  }
}

forEach( Arrays.asList(1, 2, 3, 4, 5), (Integer i) -> System.out.println(i) );

Function

@Functionallnterface
public interface Function<T, R> {
  R apply(T t);
}

public static <T, R> List<R> map(List<T> list,
Function<T, R> f) {
  List<R> result = new ArrayList<>();
  for (T s : list) {
    result.add(f.apply(s));
  }
  return result;
}

List<Integer> l = map(
                          Arrays.asList("lambdas", "in", "action"), (String s) -> s.length()
                      );

기본형 특화

기본형 특화

함수형 인터페이스와 람다 요약

함수형 인터페이스와 람다 요약-1

함수형 인터페이스와 람다 요약-2

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

형식 검사

형식 검사

같은 람다, 다른 함수형 인터페이스

Comparator<Apple> c1 =
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2. getWeight());
TolntBiFunction<Apple, Apple> c2 =
(Apple a1, Apple a2) -> a1.getWeight().compareTo (a2.getWeight());
BiFunction <Apple, Apple, Integer> c3 =
(Apple a1, Apple a2) -> a1.getWeight ().compareTo(a2.getWeight());
// Predicate는 불 린 반환값을 강는다.
Predicate<String> p = s -> list.add(s);
// Consumer는 void 반환값을 갚는다.
Consumer<String> b = s -> list.add(s);

형식 추론

// 형식을 추론하지 않음
Comparator<Apple> c = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

// 형식을 추론함
Comparator<Apple> c = (a1, a2) -> a1.getWeight().compareTo(a2.getWeight());

지역 변수 사용

메서드 레퍼런스

요약

메서드 레퍼런스-요약

메서드 레퍼런스를 만드는 방법

  1. 정적 메서드 레퍼런스
    • Integer의 parseInt 메서드는 Integer::parseInt
  2. 다양한 형식의 인스턴스 메서드 레퍼런스
    • String의 length 메서드는 String::length
  3. 기존 객체의 인스턴스 메서드 레퍼런스
    • Transaction 객체에 getValue 메서드가 있다면, expensiveTransaction::getValue

메서드 레퍼런스를 만드는 방법

생성자 레퍼런스

Supplier<Apple> c1 = Apple::new;
Apple a1 = c1.get();

// 위 코드는 다음과 같다.
Supplier<Apple> c1 = () -> new Apple();
Apple a1 = c1.get();
Function<Integer , Apple> c2 = Apple::new;
Apple a2 = c2 . apply(110);

// 위 코드는 다음과 같다.
Function<Integer, Apple> c2 = (weight) -> new Apple(weight);
Apple a2 = c2 . apply(110);
List<Integer> weights = Arrays.asList(7 , 3 , 4 , 10);
List<Apple> apples = map(weights, Apple::new);

public static List<Apple> map(List<Integer> list, Function<Integer, Apple> f) {
  List<Apple> result = new ArrayList<>();
  for(Integer e : list) {
    result.add(f.apply(e));
  }
  return result;
}

람다, 메서드 레퍼런스 활용하기!

1단계: 코드 전달

public class AppleComparator implements Comparator<Apple> {
  public int compare(Apple al, Apple a2) {
    return al.getWeight().compareTo(a2.getWeight());
  }
}
inventory.sort(new AppleComparator());

2단계: 익명 클래스 사용

inventory.sort(new AppleComparator() {
  public int compare(Apple al, Apple a2) {
    return al.getWeight().compareTo(a2.getWeight());
  }
});

3단계: 람다 표현식 사용

inventory.sort((Apple al, Apple a2) -> al.getWeight().compareTo(a2.getWeight()));

// 위 코드 다음 코드로 간소화(형식 추론)
inventory.sort((al, a2) -> al.getWeight().compareTo(a2.getWeight()));

// 위코드 한번 더 간소화
// 1단계
Comparator<Apple>) c = Comparator.comparing((Apple a) -> a.getWeight());
// 2단계
inventory.sort(comparing((a) -> a.getWeight()));

4단계: 메서드 레퍼런스 사용

inventory.sort(comparing(Apple::getWeight));

람다 표현식을 조합할 수 있는 유용한 메서드

Comparator 조합

// 정렬
Comparator<Apple>) c = Comparator.comparing(Apple::getWeight);

// 역정렬
Comparator<Apple>) c = Comparator.comparing(Apple::getWeight).reversed());;

// Comparator 연결
inventory.sort(comparing(Apple::getWeight)
        .reversed()
        .thenComparing(Apple::getCountry));

Predicate 조합

// 결과 반전
Predicate<Apple> notRedApple = redApple.negate();

// 두 Predicate 객체 연결
Predicate<Apple> redAndHeavyApple = redApple.and(a -> a.getWeight() > 158) ;

Function 조합

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); // 4 반환

// 위인 인수전달과 반대로 전달
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.compose(g);
int result = h.apply(1); // 3반환

요약