람다를 어디에 사용할수 있을까?
함수형 인터페이스
정확히 하나의 추상메서드를 지정하는 인터페이스
- 함수형 인터페이스로 뭘 할수 있을까?
- 람다 표현식으로 함수형 인터페이스의 추상메서드 구현을 직접 전달할 수 있으므로
전체 표현식을 함수형 인터페이스의 인스턴스 취급
- 익명 클래스로도 같은 기능을 구현 가능하다.
- 람다 표현식으로 함수형 인터페이스의 추상메서드 구현을 직접 전달할 수 있으므로
public class Main {
public static void main(String[] args) {
Runnable r1 = () -> System.out.println("Hello 1");
Runnable r2 = new Runnable() {
@Override
public void run() {
System.out.println("Hello 2");
}
};
process(r1);
process(r2);
process(() -> System.out.println("Hello 3"));
}
public static void process(Runnable r){
r.run();
}
}
함수 디스크립터
함수형 인터페이스의 추상 메서드 시그니처는 람다 표현식의 시그니처를 가리킨다.
람다 표현식의 시그니처를 서술하는 메서드를 함수 디스크립터
라고 부른다.
함수형 인터페이스를 인수로 받는 메서드에만 람다 표현식을 사용할 수 있다.
실행 어라운드 패턴
자원처리에 사용하는 순환 패턴은 자원을 열고, 처리한 다음에, 자원을 닫는 순서로 이루어진다.
실제 자원을 처리하는 코드를 설정과 정리 두 과정이 둘러싸는 형태의 코드를 실행 어라운드 패턴이라고 한다.
텍스트 파일에서 텍스트를 읽고 자원을 반환하는 코드의 경우에는 아래와 같이 코드가 작성될 수 있는데, 동일 프로젝트 내에서 읽어드리는 파일과 읽어드린 데이터를 처리하는 방법만 변경될 뿐 리더를 여닫는 패턴을 비슷할 것이다
public String processFile() throws IOException {
try ( BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
return br.readLine();
}
}
특별한 void 호환규칙
람다의 바디에 일반 표현식이 있으면 void를 반환하는 함수 디스크립터와 호환된다.(물론 파라미터 리스트도 호환되어야 함)
예를 들어 다음 두 행의 예제에서 List의 add메서드는 Consumter 콘텍스트(T → void)가 기대하는 void 대신 boolean을 반환하지만 유효한 코드다.
//Predicate는 boolean 반환값을 갖음
Predicate<String> predicate = s -> list.add(s);
// Consumer는 void 반환값을 갖지만 컴파일 에러가 발생되지 않음
Consumer<String> consumer = s -> list.add(s);
지역변수 사용
람다 표현식은 익명함수가 하는 것처럼 자유변수(파라미터로 넘겨받은 변수가 아닌 외부에 정의된 변수)도 사용할 수 있는데 이를 람다 캡쳐링(capturing lambda) 이라 한다.
람다는 인스턴스 변수와 정적 변수를 자유롭게 캡쳐할 수 있지만 지역변수는 명시적으로 final로 선언되어 있거나 final을 붙인 것처럼 수정하지 말아야 한다.
// 가능
int port = 8080;
Runnable r = () -> System.out.println(port);
r.run();
//불가능
int port = 8080;
Runnable r = () -> System.out.println(port);
port=9090;
r.run();
public class Capturing {
private static int port=8080;
public static void main(String[] args) {
Runnable r = () -> System.out.println(port);
port=9090; // 인스턴스 변수는 재할당하면 안된다는 제약조건에 적용되지 않음.
r.run();
}
}
지역변수 제약
위와 같은 제약이 필요한 이유는 지역 변수는 스택에 생성, 인스턴스 변수는 힙 영역에 생성된다.
따라서 지역 변수는 쓰레드끼리 공유가 안 되며 인스턴스 변수는 쓰레드끼리 공유가 가능하다.
변수를 할당한 스레드가 사라져서 변수 할당이 해제되었는데도 람다를 실행하는 스레드에서 해당 변수를 접근하려 할 수 있어서 자바에서는 원래 변수에 접근이 아니라 자유 지역 변수의 복사본을 제공한다. 따라서 복사본의 값이 바뀌지 않아야 하므로 지역 변수는 한 번만 값을 할당해야 한다.
메서드 참조
아래에 책을 연도순으로 오름차순으로 정렬하는 코드가 있다.
public class MethodReference {
public static void main(String[] args) {
List<Book> books = asList(new Book("이펙티브 자바", "조슈아 블로크", 2018, "IT"),
new Book("모던 자바 인 액션", "라울-게이브리얼 우르마", 2019, "IT"),
new Book("이순신의 바다", "황현필", 2021, "History"),
new Book("갈매기의 꿈", "리처드 바크", 2020, "Fiction")
);
books.sort((book, t1) -> book.getYear().compareTo(t1.getYear()));
}
}
이 경우 메서드 참조를 이용하면 기존의 메서드 정의를 재활용해서 람다처럼 전달할 수 있다.
books.sort(Comparator.comparing(Book::getYear));
메서드 참조는 람다 표현식이 오직 하나의 메소드를 호출할 때 불필요한 매개변수를 제거하고 사용하도록 해준다.
참고문서
모던 자바 인 액션 - 라울 게이브리얼 우르마, 마리오 푸스코, 앨런 마이크로소포트
'프로그래밍 > Book' 카테고리의 다른 글
[모던 자바 인 액션] 7장. 병렬 데이터 처리와 성능 (0) | 2022.02.21 |
---|---|
[모던 자바 인 액션] 6장. 스트림으로 데이터 수집 (0) | 2022.02.09 |
[모던 자바 인 액션] 5장. 스트림 활용 (0) | 2022.02.06 |
[모던 자바 인 액션] 4장. 스트림 소개 (0) | 2022.01.31 |
[모던 자바 인 액션] 2장. 동작 파라미터화 (0) | 2022.01.24 |