공부일기/JAVA

JAVA 공부일기2-3

고다나 2023. 5. 17. 19:03
모던 자바 인 액션
Chapter3 람다 표현식

 

3.1. 람다란 무엇인가?

람다 표현식: 메서드로 전달할 수 있는 익명 함수를 단순화한 것. 파라미터, 화살표, 바디로 이루어짐.

 

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

 

함수형 인터페이스

함수형 인터페이스: 하나의 추상메서드를 지정하는 인터페이스.

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

Runnable r1 = () -> System.out.println("Hello World 1"); //람다 사용

Runnable r2 = new Runnable(){ //익명 클래스 사용
	public void run(){
    	System.out.println("Hello World 2");
    }
};

public static void process(Runnable r){
	r.run();
}

process(r1); //'Hello World 1' 출력
process(r2); //'Hello World 2' 출력
process(() -> System.out.pringln("Hello World 3")); // 직접 전달된 람다 표현식으로 'Hello World 3' 출력

 

함수 디스크립터

함수 디스크립터: 람다 표현식의 시그니처를 서술하는 메서드.

(*메서드 시그니처: 자바에서 프로그래머가 디자인한 메서드 구조를 의미하며 메서드 이름 파라미터 리스트로 구성)

ex) 위의 예시에서 () -> System.out.pringln("Hello World 3")은 인수가 없으며 void를 반환하는 람다 표현식. 이는 Runnable 인터페이스의 run 메서드 시그니처와 같음.

 

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

실행 어라운드 패턴: 로직 앞뒤로 설정(준비)/정리(마무리) 로직이 들어가는 경우.

 

예시) 파일을 읽는 메서드를 유연하게 만드는 과정.

public String processFile() throws IOException {
	try (BufferedReader br = 
    		new BufferedReader(new FileReader("data.txt"))){
    	return br.readLine(); //processFile()의 동작이 정해져있음.
    }
}
//BufferedReader -> String과 IOException을 던질 수 있는 시그니처와 일치하는 함수형 인터페이스
@FunctionalInterface
public interface BufferedReaderProcessor{
	String process(BufferedReader b) throws IOException;
}

//BufferedReader를 이용해서 다른 동작을 수행할 수 있도록 processFile 메서드로 동작 전달
//함수형 인터페이스를 이용해서 동작 전달
public String processFile(BufferedReaderProcessor p) throws IOException {
	try(BufferedReader br = 
    		new BufferedReader(new FileReader("data.txt"))){
	return p.process(br);
    }
}
//람다를 이용해서 다양한 동작을 processFile 메서드로 전달
String oneLine = processFile((BufferedReader br) -> br.readLine());

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

 

3.4 함수형 인터페이스 사용

자바8 설계자들은 java.util.function 패키지로 새로운 함수형 인터페이스 제공.

 

Predicate<T> 인터페이스:  제네릭 형식 T의 객체를 인수로 받아 boolean을 반환하는 추상메서드 test 정의.

Consumer<T> 인터페이스: 제네릭 형식 T의 객체를 인수로 받아 void를 반환하는 추상 메서드 accept 정의. 

Function<T, R> 인터페이스: 제네릭 형식 T의 객체를 인수로 받아 제네릭 형식 R 객체를 반환하는 추상 메서드 apply 정의. 

 

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

 

형식검사

람다 표현식을 사용할 때 진행되는 형식 확인 과정 순서 

List<Apple> heavierThan150g = filter(inventory, (Apple apple) -> apple.getWeight() > 150);

① filter 메서드의 선언 확인.

② filter 메서드는 두 번째 파라미터로 Predicate<Apple> 형식을 기대함.

③ Predicate<Apple>은 test라는 한 개의 추상 메서드를 정의하는 함수형 인터페이스임.

④ test 메서드는 Apple을 받아 boolean을 반환하는 함수 디스크립터 묘사.

⑤ filter 메서드로 전달된 인수는 이와 같은 요구사항을 만족해야 함.

=> 위 예제에서 람다 표현식은 Apple을 인수로 받아 boolean을 반환하므로 유효한 코드.

 

형식추론

자바 컴파일러는 람다 표현식이 사용된 콘텍스트를 이용해서 람다 표현식과 관련된 함수형 인터페이스를 추론. 즉 람다 파라미터 형식을 추론할 수 있으므로 생략 가능.

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

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

 

지역 변수 사용

람다 캡처링: 람다 표현식에서는 익명 함수가 하는 것처럼 자유 변수(파라미터로 넘겨진 변수가 아닌 외부에서 정의된 변수)를 활용할 수 있음. 람다는 인스턴스 변수와 정적 변수를 자유롭게 캡처할 수 있지만, 단 지역 변수는 명시적으로 final로 선언되어 있거나, 한 번만 할당되어야 함.

 

3.6 메서드 참조

메서드 참조는 특정 메서드만을 호출하는 람다의 축약형. 기존 메서드 구현으로 람다 표현식을 만들 수 있음. 

① 정적 메서드 참조

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

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

모던자바인액션 p117