Java

람다 표현식

내용

  • 람다란 무엇인가?
  • 어디에, 어떻게 람다를 사용하는가?
  • 실행 어라운드 패턴
  • 함수형 인터페이스, 형식 추론
  • 메서드 참조
  • 람다 만들기

람다란 무엇인가?

메서드로 전달할 수 있는 익명 함수를 단순화한 것
  • 이름은 없지만
    • 파라미터 리스트
    • 바디
    • 반환 형식
    • 발생할 수 있는 예외 리스트
    • 는 가질 수 있다.

특징

  • 익명
    • 보통의 메서드와 달리 이름이 없다.
  • 함수
    • 메서드처럼 특정 클래스에 종속되지 않으므로 함수라고 부른다.
    • 하지만 메서드처럼 파라미터 리스트, 바디, 반환 형식, 가능한 예외 리스트를 포함한다.
  • 전달
    • 람다 표현식을 메서드 인수로 전달하거나 변수로 저장할 수 있다.
  • 간결성
    • 익명 클래스처럼 많은 자질구레한 코드를 구현할 필요가 없다.

필요성

  • 간결성
    • // 이전 코드 Comparator<Apple> byWeight = new Comparator<Apple>() { public int compare(Apple a1, Apple a2) { return a1.getWeight().compareTo(a2.getWeight()); } }
      // 개선 코드 Comparator<Apple> byWeight = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

구조

notion image
  • 파라미터 리스트
  • 화살표
    • 람다의 파라미터 리스트와 바디를 구분한다.
  • 람다 바디
    • 람다의 반환값
  • 여러 줄 가능
    • notion image
  • 표현식 스타일(Expression Style)
    • (parameters) -> expression
  • 블록 스타일(Block-Style)
    • (parameters) -> { statements; }

예제

notion image

함수형 인터페이스

정확히 하나의 추상 메서드를 지정하는 인터페이스
  • Comparator, Runnable 등
  • 디폴트 메서드를 포함해도 추상 메서드가 오직 하나면 함수형 인터페이스

이것으로 무엇을 할 수 있는가?

람다 표현식으로 함수형 인터페이스의 추상 메서드 구현을 직접 전달할 수 있으므로, 전체 표현식을 함수형 인터페이스의 인스턴스로 취급(함수형 인터페이스를 구현한 클래스의 인스턴스)할 수 있다.

함수 디스크립터 : Function Descriptor

함수형 인터페이스의 추상 메서드 시그니처는 람다 표현식의 시그니처를 가리킨다.
함수형 인터페이스의 추상 메서드 시그니처

@FunctionalInterface

함수형 인터페이스가 아니면 컴파일러가 에러를 발생시킨다.

활용

실행 어라운드 패턴

notion image
  • 자원 처리하는 코드 등
  1. 원래 코드
public static String processFileLimited() throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(FILE))) { return br.readLine(); } }
  1. 함수형 인터페이스 선언
public interface BufferedReaderProcessor { String process(BufferedReader b) throws IOException; }
  1. 함수형 인터페이스를 받을 수 있도록
public static String processFile(BufferedReaderProcessor p) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(FILE))) { return p.process(br); } }
  1. 람다 전달
String oneLine = processFile((BufferedReader b) -> b.readLine()); String twoLines = processFile((BufferedReader b) -> b.readLine() + b.readLine());

함수형 인터페이스 사용

Predicate

java.util.function.Predicate<T> 인터페이스
  • test라는 추상 메서드를 정의
  • 제네릭 형식 T의 객체를 인수로 받아 불리언 반환
@FunctionalInterface public interface Predicate<T> { boolean test(T t); } public static List<Apple> filter(List<Apple> inventory, ApplePredicate p) { List<Apple> result = new ArrayList<>(); for (Apple apple : inventory) { if (p.test(apple)) { result.add(apple); } } return result; } Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty(); List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);

Consumer

java.util.function.Consumer<T> 인터페이스
  • accept라는 추상 메서드 정의
  • 제네릭 형식 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

java.util.function.Function<T, R> 인터페이스
  • apply라는 추상 메서드 정의
  • 제네릭 형식 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; } List<Integer> l = map(Arrays.asList("lambdas", "in", "action"), (String s) -> s.length() ); // [7, 2, 6]
 
 
 
 
 
 
 
 
 

출처