[모던 자바 인 액션] 람다 표현식

2023. 10. 12. 00:04JAVA

해당 글은 모던 자바 인 액션 도서의 3장 람다 표현식에서 나오는 키워드 용어들을 적고 제 생각대로 작성한 글입니다. 
틀린 내용이 있을 수 있습니다.

 

동작 파라미터화

  • 함수형 인터페이스를 매개변수로 전달하여 실행시키는 것
  • 메소드의 동작을 외부에서 보이지 않게 캡슐화한 다음에 메소드로 전달해서 수행하는것
  • 매개변수를 받은 메소드는 동작 파라미터화된 함수형 인터페이스의 내부를 모른체로 실행시키기만 하면 됩니다.

 

람다 표현식

  • 이름이 없는 함수
  • 메소드로 전달할 수 있는 익명 함수를 단순하게 표현한 표현식

 

람다 표현식은 어디에 사용되는가?

  • 함수형 인터페이스에서 사용할 수 있습니다.
  • 예를 들어 스트림의 filter 메소드의 매개변수로 Predicate<T>를 기대하는데 람다 표현식으로 Predicate를 표현할 수 있습니다.

 

함수형 인터페이스

  • 하나의 추상 메소드를 가지는 인터페이스
  • 대표적으로 Comparator, Runnable, Predicate가 있다.
  • 디폴트 메소드가 있어도 추상 메소드가 단 하나면 함수형 인터페이스가 될 수 있다.

 

디폴트 메소드

  • 인터페이스에서 기본적으로 구현되어 있는 메소드
  • 디폴트 메소드를 활용하면 인터페이스를 구현하는 일부 클래스가 의무적으로 재정의하지 않아도 된다.

 

함수 디스크립터

  • 람다 표현식의 시그니처를 명세한 것

 

@FunctionalInterface

  • 해당 인터페이스가 함수형 인터페이스라고 가리키는 애노테이션
  • @FunctionalInterface를 명시한 인터페이스가 함수형 인터페이스를 만족하지 않으면 컴파일러가 에러를 발생시킨다.

 

실행 어라운드 패턴(execute around pattern)

  • 실제 자원을 처리하는 코드를 설정과 정리 두 과정이 둘러싸서 실행하는 패턴
@FunctionalInterface
public interface BufferedReaderProcessor {
	String process(BufferedReader b) throws IOException;
}

public class FileService {
	public String profileFile_v3(BufferedReaderProcessor p) throws IOException {
			File file = ResourceUtils.getFile("classpath:data.txt");
			try(BufferedReader br = new BufferedReader(new FileReader(file))){
				return p.process(br);
			}
	}
}

 

Predicate 함수형 인터페이스

  • 제네릭 타입 변수 한개를 입력받아서 boolean 타입을 반환하는 함수형 인터페이스
  • 입력값이 특정 조건에 만족하는지 검사하는데 사용하는 함수형 인터페이스

 

Consumer 함수형 인터페이스

  • 제네릭 타입 인스턴스를 한개 입력받아서 void 반환하는 함수형 인터페이스
  • 입력값을 받아서 어떤 동작을 수행할 때 사용한다.

 

Function 함수형 인터페이스

  • 제네릭 타입 T 인스턴스 한개 입력받아서 제네릭 타입 U 인스턴스를 반환하는 함수형 인터페이스
  • 입력값을 받아서 어떤 출력값으로 반환할때 사용한다.

 

예외, 람다, 함수형 인터페이스의 관계

  • 함수형 인터페이스는 확인된 예외를 발생시키는 것을 허용하지 않습니다.
  • 예외를 던지는 람다 표현식을 만들려면 함수형 인터페이스의 추상 메소드 선언부에 확인된 예외를 선언하거나 람다를 try/catch문으로 감싸야 한다.
@FunctionalInterface
public interface BufferedReaderProcessor {
	String process(BufferedReader b) throws IOException;
}

Function<BufferedReader, String> f = (BufferedReader b) -> {
	try{
		return b.readLine();
	}catch(IOException e){
		throw new RuntimeException(e);
	}
};

 

대상 형식(target type)

  • 문맥에서 기대되는 람다 표현식의 형식을 대상 형식이라 한다.
List<Apple> heavierThan150g = filter(inventory, (Apple apple) -> apple.getWeight() > 150);
  • filter의 두번째 매개변수는 함수형 인터페이스입니다.
  • 매개변수 타입이 Apple인것을 알 수 있습니다.
  • 반환 타입이 boolean 타입인것을 알 수 있습니다.
  • 즉, Predicate 함수형 인터페이스의 조건을 만족하는 람다 표현식이 됩니다.

 

형식 추론

  • 람다 표현식에 사용된 문맥(대상 형식)을 이용해서 람다 표현식이 의미하는 함수형 인터페이스를 추측하는 것
Comparator<Apple> c = (a1, a2) -> a1.getWeight().compareTo(a2.getWeight());
  • Comparator<Apple>이라는 문맥을 이용하여 a1, a2의 타입이 Apple인 것을 추측할 수 있습니다.

 

람다 캡처링

  • 람다 표현식 안에서 람다 바디 안에서 사용하는 지역 변수가 아닌 외부의 변수를 참조하는 것
int portNumber = 1337;
Runnable r = () -> System.out.println(portNumber);
  • 람다 표현식 입장에서 portNumber는 람다 바디에 정의된 지역 변수가 아닌 외부에 정의된 portNumber 변수를 참조하여 출력합니다.
  • 람다 캡처링의 제약은 람다 바디에서 참조된 외부 변수는 final로 선언된 변수와 같이 이후에 변해서는 안된다는 제약을 갖고 있습니다.
int portNumber = 1337;
Runnable r = () -> System.out.println(portNumber);
portNumber = 1338; (x)
  • 람다 표현식에서 외부 변수인 portNumber를 참조하였는데 이후에 portNumber의 값을 변경하려고 합니다. 컴파일러 에러가 발생합니다.
  • 인스턴스는 힙에 저장되고 지역 변수는 스택에 저장됩니다. 람다가 스레드에서 실행된다면 지역 변수를 할당한 스레드가 사라져서 지역 변수 할당이 해제되었는데도 람다를 실행하는 스레드에서 이미 지역 변수 할당이 해제된 변수에 접근할 가능성이 있습니다. 그래서 원래 지역 변수에 접근하는 것이 아니라 지역 변수의 복사본을 람다가 있는 스레드에 제공합니다. 따라서 지역 변수의 복사본이 변경되지 않아야 한다는 제약이 생겼습니다.

 

메소드 참조

  • 메소드 참조는 특정한 메소드만을 호출하는 람다의 축약형
(Apple apple) -> apple.getWeight() => Apple::getWeight