JANGUN


자바 8 람다의 힘
Functional Programming in Java 8


지음 : 벤컷 수브라마니암
옮김 : 장독대



목차

chapter 1 Hello, Lambda Expressions!
chapter 2 Using Collections
chapter 3 String, Comparator, and Filter
chapter 4 Designing with lambda Expressions
chapter 5 Working with Resources
chapter 6 Being Lazy
chapter 7 Optimizing Recursions
chapter 8 Composing with Lambda Expressions
chapter 9 Bringing it All Together
Appendix
1 Starter Set of Functional Interfaces
2 Syntax Overview
3 Web Resources


chapter 1 헬로, 람다 표현식

코딩 스타일은 지금 파격적인 변신을 준비하고 있다.
- 오류 발생할 가능성을 현저히 줄이고
- 간결(간단)해지고
- 다양한 기능을 접목할 수 있고
- 이해하기 쉬운 서술적인 코드로 작성하고
- 디자인 패턴 또한 간단히 구현할 수 있다.

사고의 전환
- 명령형 스타일 – 개발하려고 하는 작업의 모든 과정을 직접 코드로 작성해야 하며, 각 과정이 제대로 실행되는 지 매번 확인해야 한다. 즉, 시시콜콜한 내용까지도 코드로 작성해야 한다.
- 서술형 스타일 – 특정 작업에 대해 프로그래밍하기 위해서 어떻게 해야 한다는 것을 자세히 설명하기보다는 원하는 것은 바로 이것이다라고 딱 부러지게 표현하는 코딩 스타일이다.

예제 코드 : 도시 이름에서 Chicago라는 도시 이름 검색
- 명령형 : 로우 레벨 프로그래밍 방식
- 서술형

개선된 점
- 난잡한 가변 변수의 사용을 방지
- 이터레이션을 단계별로 래핑
(이터레이션 코드가 외부로 드러나지 않기 때문에 개발자는 이터레이션 자체코드에 대해서는 신경쓸 필요 없음)
- 어수선한 코드의 사용을 막아줌
- 코드에 대한 설명이 명확해짐 : 개발자가 원하는 바를 유지할 수 있음
- 비즈니스 의도는 유지하면서 코드는 명료해짐
- 오류의 발생 확률을 줄여줌
- 이해하기 쉽고 유지 보수가 쉬움

예제 코드 : price의 합계 계산. 단, $20보다 전체 price가 높으면 10% 할인.
- 명령형 스타일 : 개발자가 원하는 내용을 무조건 코드로 작성해야 한다는 “원시적인 강박관념“에 사로 잡혀 있는 것처럼 보이며, 단일 책임 원칙(Single Responsibility Principle)을 무시
- 서술형 스타일 : 고차원적인 추상화 방법을 사용

stream() 메서드 : 편리하게 사용할 수 있는 이터레이터를 제공
filter(), map() 메서드 : 파라미터로 어나니머스 함수를 가진다.
prices 리스트에 있는 각각의 price에 대해서 제공된 람다 표현식을 호출하고,
이 호출에 대한 결과를 새로운 컬렉션에 넣는다.
map()은 컬렉션의 각 엘리먼트에 대해 주어진 함수를 적용한다.
reduce() 메서드는 최종적인 결과를 얻게 하나.

향상된 점
- 코드가 어수선하지 않고 더 짜임새 있게 구성됐다.
- 로우-레벨 오퍼레이션을 사용하지 않는다
- 로직을 강화하거나 변경하기에 더 쉽다
- 메서드를 사용할 때 이터레이션은 라이브러리에 의해 제어된다.
- 더 효율적이다. 루프에 대한 lazy evaluation이 가능하다
- 원하는 부분을 병렬화하기 쉽다.

람다(Lamda) : 명령형 프로그래밍의 번거로움을 해결하는 함수형 프로그래밍 방법의 중요한 기능 중 하나

함수형 스타일 코드의 큰 이점
- 투자 대비 높은 효율의 코드를 만든다.
- 변수의 명식적인 변경이나 재할당의 문제를 피할 수 있다.
(버그의 원천, 동시성 방해를 해결. 함수형 버전에서는 코드 안에 명시적인 변경이 없다)
- 함수형 버전은 쉽게 병렬화가 가능하다. (thread-safety)
- 서술적인 코드 작성이 가능하다
- 함수형 버전은 더 간결하다. 코드의 양 자체를 줄일 수 있다.
- 직관적이다. 사람이 문제를 설명하는 방식대로 코드를 작성하면 된다. 이해하기도 더 쉽다.

함수형 스타일은 객체 지향 프로그래밍 방법을 거스르는 것이 아니라, 실제 패러다임의 변화는 명령형 스타일에서 서술적 스타일로 변경됐다는 것이다.
자바8에서 프로그래밍의 함수형 스타일과 OOP 스타일을 효과적으로 혼합하여 사용할 수 있다.
OOP스타일을 사용하여 Domain Entity, State, Relationship을 모델링하고,
함수를 사용하는 것처럼 Behavior, State Transformation, Business Workflow, Data Processing을 모델링할 수 있다.

간결하다는 것은 짧고, 오류가 없고, 개발자의 의도를 효과적으로 전단한다는 것이다.
코드를 작성하는 것은 여러 가지 식재료를 한 솥에 넣고 하나의 음식으로 만드는 작업과 같다.
간결한 코드는 Design Agility와 같은 의미다.

함수형 스타일로 코딩해야 하는 이유
- 제약 조건을 잘 관리하는 작은 코드 조각으로 이러한 결정을 캡슐화할 수 있다.
- 문제의 핵심은 이터레이션! 내부 이터레이터를 제공.
- 정책을 강화하는 함수를 제공 : 상태를 체크하는 정책과 감시 기록을 업데이트하는 작업을 추상화하고 캡슐화한다.
- 정책 확장하기 : 함수형 인터페이스와 람다 표현식을 사용하여 정책을 설계한다. 코드의 behavior에 집중한다.
- 손쉬운 병렬화 : 순차 구로로 되어 있는 거대한 모듈들의 코드를 동시에 실행할 수 있는 버전으로 바꾸어 준다.
- 스토리텔링 : 문제를 설명하는 방식대로 코딩하게 해준다.
- 문제의 분리 : 애플리케이션에서 공통적으로 필요한 것은 코어 연산과 연산에 필요한 세밀한 로직을 분리하는 것이다.
OOP에서는 Strategy Pattern을 사용하여, 이러한 문제의 분리를 해결한다. 해야 할 일은 한 개 이상의 인터페이스를 만들고 그 인터페이스를 구현하기 위한 몇 개의 클래스를 생성하는 것이다.
- Lazy Evaluation : 당장 필요하지 않은 작업의 실행은 잠시 연기시킨다.
- 테스트성 향상시키기 : 변경이 덜한 코드가 오류를 발생시키는 일도 드물다.

혁명이 아닌 진화 : 함수형 스타일의 프로그래밍 방법은 모든 언어에 포함되고 있다. (C++, C# 등등)

함수형 기능 구현의 가이드 라인
(1) 서술적
(2) 불변성의 증진
(3) 사이드 이펙트의 회피
(4) 단순 문법보다는 표현에 주력
(5) 고차 함수를 사용한 설계

서술적 (Be Declarative)
익숙한 명령형 스타일의 코드는 가변적이고 명령어 위주의 프로그래밍이다. 변수나 객체를 생성하고 이들의 상태를 수정한다. 또한 실행하기 위한 자세한 명령어들과 인스트럭션을 제공한다.
불변 컬렉션을 이용하여 서술적인 함수들을 사용하면, 이터레이션을 제어하는 것과 같은 노력이 많이 들고 주의 깊게 작성해야 하는 코드와 로우 레벨의 코드들은 라이브러리의 함수들을 사용하여 처리하도록 하고 그 세부적인 코드까지는 신경 쓸 필요가 없다.
불변 컬렉션과 서술적 프로그래밍은 함수형 프로그래밍 스타일의 원천 기술이다.

불변성의 증진 (Promote Immutability)
변경할 수 있는 코드는 움직이는 것들이 많다. 다중 변수의 변경이 있는 코드는 이해하기 어렵고 병렬화하기 까다롭다.
불변성은 근본적으로 이런 문제를 제거한다. 자바는 불변성을 지원하지만 강제하지는 않다.
가능한 불변 객체를 사용해야 한다. (예. String 객체)
컬렉션을 사용할 때 불변 혹은 수정 불가능한 컬렉션을 생성하자 (Arrays.asList(), Collections 클래스의 unmodifiableList() 등)
가변성을 피하기 위해서 순수 함수를 생성할 수도 있다. 단 이 함수는 사이드 이펙트가 없는 함수여야 한다.

사이드 이펙트의 회피
사이드 이펙트가 없는 함수는 불변이며 함수를 사용하는 중에 함수에 대한 입력의 변경이 없다.
이 함수들은 이해하기 쉽고 오류의 발생 가능성이 낮으며 더 쉽게 최적화할 수 없다.
사이드 이펙트가 없다면 레이스 컨디션의 문제나 동기화 업데이트 문제도 해결해준다.

문법보다는 표현에 주력
문법적인 문장은 이해하기 어렵고 가변성 문제를 일으킨다.
서술문을 사용하면 가변성이 줄어들고 함수들을 혼합하여 사용할 수 있다.
서술문을 사용하여 설계하고, 이 서술문을 이용하여 코딩한다. 간결한 코드와 쉽게 이해할 수 있는 코드가 생성된다.
코드는 논리적인 흐름에 따라 구성되며 개발자가 문제를 설명하는 순서대로 프로그래밍을 할 수 있다.
간결한 버전을 사용하면 해결해야 할 문제가 변경됐을 때 코드를 수정하기 쉽다

고차 함수를 사용한 설계
자바는 변수를 개발자의 의지대로 수정할 수 있다. 자바는 완전한 함수형 프로그래밍 언어는 아니다.
그러나 고차 함수(high-order function)를 사용하여 함수형 스타일로 코드를 작성할 수 있다.
고차 함수는 현재 상태가 아닌 이후의 상태에서도 현재 사용하는 함수를 재사용할 수 있도록 해주는 개념이다.
재사용을 하기 위해 객체와 클래스에만 의존하는 대신 고차 함수를 사용하면 작고, 집중되어 있고, 두 개 이상을 결합할 수 있으며, 잘 작성된 함수들을 쉽게 재사용할 수 있다.
OOP에서는 객체를 메서드에 넘기곤 했다. 내부에 메서드를 포함하고 있는 객체를 생성하고 그 메서드는 객체를 리턴한다.
고차 함수는 메서드가 객체에 대해 무엇을 하는가를 함수에게 전달한다.
- 함수를 파라미터로 함수에게 전달한다
- 함수 내부에서 함수를 생성한다
- 함수로부터 함수를 리턴한다

예제에서 map에 대한 매개변수로 price -> price.multiply(BigDecimal.valueOf(0.9)) 함수를 넘긴다.
전달된 함수는 그 후에 바로 생성된다. 그 시점은 고차 함수 map에 호출되는 순간이다.
일반적으로 함수는 본문, 이름, 파라미터 리스트, 그리고 리턴 타입으로 되어 있다.
막 생성된 함수는 화살표로 된 파라미터 리스트를 갖고 있고, 짧은 본문도 있다.
파라미터의 타입은 자바 컴파일러에 의해 결정되며 리턴 타입은 묵시적 타입이다.
이 함수는 Anonymous이며 이름도 없다. 이 함수를 Anonymous 함수라고 말하지는 않고 대신 이것을 람다 표현식이라고 부른다.

Anonymous 함수를 넘기는 것은 이전 자바에서는 알려지지 않은 개념이다.
지금까지 사용해온 방식은 Anonymous 클래스에 인스턴스를 넘기는 방식이었다. Anonymous 클래스가 단지 하나의 매서드만을 갖고 있다면, 아직 클래스를 생성하는 작업을 계속해야 한다. 비록 Anonymous 라고 해도 클래스를 초기화해야 한다.
대신에 람다 표현식을 사용하면 자바에서는 경량화된 문법을 사용한다. 객체를 이용한 추상화 개념을 사용할 수 있다.
이제 추상화 개념에 람다 표현식을 사용한 추상화 Behavior를 결합해보자.

자바는 어떻게 함수를 인수로 받는 것일까?

투자 대비 효율의 극대화

과거에는 하나의 메서드만을 갖는 인터페이스를 사용해왔다. Runnable, Callable, Comparable 등
이러한 인터페이스는 라이브러리에서 공통적으로 사용되며 하나의 함수만을 사용하는 경우에 주로 사용된다.
하나의 메서드 인터페이스를 사용하는 모든 기존의 라이브러리 메서드들은 이제 경량화 함수를 사용하게 됐다.

함수형 인터페이스는 하나의 추상화 메서드를 갖는 인터페이스다. Function, Predicate, Consumer, Supplier 등
함수형 인터페이스는 또한 0 또는 정적 메서드 그리고 디폴트 메서드를 가지며 이것들은 인터페이스 내부에 구현된다.
@FunctionalInterface 어노테이션으로 함수형 인터페이스를 마킹할 수 있다.

메서드가 파라미터로 함수형 인터페이스를 취하면, 다음과 같은 것들을 넘길 수 있다.
- Anonymous Inner Class (구식 방법, 사용할 일 없음)
- 람다 표현식 (map() 메서드 호출했던 방법)
- 메서드 혹은 생성자 레퍼런스 (뒤에 보자)

메서드에 대응되는 파라미터가 함수형 인터페이스의 레퍼런스라면, 컴파일러는 인수로 람다 표현식이나 메서드/생성자를 받는다.
람다 표현식을 메서드에 넘길 때, 컴파일러는 람다 표현식을 적절한 함수형 인터페이스의 인스턴스로 변환한다. 이러한 변환은 내부 클래스에는 거의 발생하지 않는다.
이 인스턴스와 합성된 메서드는 매개변수에 대응되는 함수형 인터페이스의 추상 메서드를 따른다.
예를 들어, map() 메서드는 함수형 인터페이스 Function <T, R>을 파라미터로 취한다. map() 메서드에 대한 호출에서 자바 컴파일러는 이것을 합성한다.

람다 표현식의 파라미터는 인터페이스의 추상 메서드의 파라미터와 매칭되어야 한다.
이 합성된 메서드는 람다 표현식의 결과를 리턴한다.
리턴 타입이 직접 추상 메서드의 결과에 매칭되지 않다면, 합성된 메서드는 결과 값을 적절하게 할당 가능한 타입으로 변경할 수 있다.
람다 표현식이 매개변수로 메서드에 어떻게 넘어가는지 알아봤다. 이해했나???

다음에는 변경되는 것이 무엇이고 람다 표현식 자체에 대해서 알아보도록 하자

정리)
이제 많은 기능을 갖는 함수형 스타일과 고차 함수를 사용하여 프로그램을 작성할 수 있다.
자바 컴파일러는 함수형 인터페이스가 원하는 곳으로 람다 표현식이나 메서드 레퍼런스를 보낼 수 있게 해준다.



chapter 2 컬렉션의 사용

컬렉션을 이터레이션하고, 새로운 컬렉션으로 변형시키기도 하고, 컬렉션에서 엘리먼트를 추출하거나 그 엘리먼트들을 쉽게 연결하는 방법을 알아보자.
간결하고, 서술적이며, 우아하고 훨씬 확장하기 쉬운 형태로 프로그래밍하자.

리스트를 사용한 이터레이션

두 가지의 명령형 스타일 코드
- 두 번째 방법은 내부적으로 Iterator 인터페이스를 사용하고 hasNext()와 next() 메서드를 호출한다.

함수형 스타일로 바꿔야 되는 이유
- for 루프는 본질적으로 순차적인 방식이라 병렬화하기가 어렵다.
- 이러한 루프는 비다형적이다. 우리가 원하는 것만을 정확하게 얻는다.
- 설계 레벨에서부터 이러한 코드는 ‘요구하지 말고 설명해봐’라는 원칙을 만족시키지 못한다.

함수형 스타일
- Iterable 인터페이스는 forEach()라는 메서드를 제공하여 더 강력해졌다.
- 이 메서드는 Consumer 타입의 파라미터를 받는다.
- Consumer의 인스턴스는 accept() 메서드를 통해 얻은 자원을 소비하는 기능을 한다.

익숙한 Anonymous inner Class 신택스 형태로 forEach() 메서드를 활용한 코드
- friends 컬렉션에서 forEach()를 호출하고 이 메서드에 Consumer의 Anonymous 인스턴스를 파라미터로 넘긴다.
- forEach() 메서드는 컬렉션에서 각 엘리먼트에 대해 주어진 Consumer의 accept() 메서드를 호출하고 원하는 작업을 수행한다.
- 이점은 어떻게 각 엘리먼트를 이터레이션하는 지에 대해서는 특별히 생각할 필요 없이 각 엘리먼트에서 해야 할 작업에만 초점을 맞출 수 있다.

위의 코드는 좀 더 장황해졌다.
람다 표현식과 새로운 컴파일러의 기능을 통해 쉽게 수정할 수 있다.
- 코드의 양을 줄이고, 하고자 하는 작업은 더욱 분명해졌다.
- forEach() 메서드는 람다 표현식 혹은 코드 블록을 인수로 받는 고차 함수이며 리스트에 있는 각 엘리먼트의 내용을 처리한다.
- 변수 name은 호출하는 동안 컬렉션의 각 엘리먼트의 name의 값을 저장하는 역할을 한다.
- 내부 라이브러리는 람다 표현식의 동작에 대한 제어를 맡게 된다.
- 이 람다 표현식을 호출하는 순서와 상관없이 레이지(lazy: 실행순서를 변경할 수 있다는 의미)하게 실행되도록 할 수 있어서 병렬화가 가능하다.
- 내부 이터레이션 방법을 사용하면, 이터레이션을 제어하기 위해 이터레이션의 실행 순서 혹은 반복되는 횟수 등에 초점을 맞추기보다는 오히려 우리가 각 엘리먼트에서 얻으려고 하는 것, 즉 엘리먼트를 사용해서 하고자 하는 목적에 초점을 맞출 수 있다. ---- 이것이 서술적(Declarative)이다.

제약 : 한번 forEach 메서드를 실행하게 되면, 이전의 두 버전과는 달리 내부에서 이터레이션을 멈출 수 없다. (해결하는 기능도 있음)
- 이러한 스타일은 컬렉션의 엘리먼트를 모두 처리해야 하는 경우와 같이 일반적인 케이스에 유용하다.
- 람다 표현식에서는 모든 파라미터의 타입을 설정하거나 아예 어떤 타입도 설정하지 않아야 한다.
- 싱글 파라미터 람다 표현식에서 파라미터의 타입을 추론할 수 있는 경우에는 파라미터 주변의 괄호를 생략할 수 있도록 자바 컴파일러가 처리한다.
- 일반적으로 함수의 파라미터를 수정하는 것은 바보 같은 짓이며 오류를 발생시키는 요인이 된다.
따라서 final로 마킹하는 것은 좋은 방법이다.
- 타입을 설정하지 않고 컴파일러가 추론하게 되면 람다 표현식 내에서 파라미터를 수정하지 않도록 상당히 주의해야 한다.
- 컴파일러는 설정되지 않은 파라미터의 타입은 추론할 수 있지만, 람다 표현식 내부에서 파라미터들이 변경되어도 상관없는지에 대한 부분까지는 알지 못한다.

메서드 레퍼런스(Method Reference)
- 자바에서는 코드의 본문을 우리가 선택한 메서드 이름으로 변경할 수 있다.
- ‘완벽은 더 추가할 것이 없을 때가 아니라 더 뺄 것이 없을 때 이루어진다‘

컬렉션에서 람다 표현식을 사용하면 이터레이션을 더 컴팩트하게 만들 수 있다.
람다 표현식이 어떻게 가변성을 제거하고 컬렉션을 변경하는 경우에 코드를 얼마나 더 간결하게 만들 수 있는지에 대해 알아보자.

리스트 변형
- 컬렉션을 사용해서 다른 결과물, 즉 다른 형태의 컬렉션을 만들어 내는 작업은 컬렉션의 엘리먼트들을 이터레이션하는 것만큼이나 쉽다.
- 원본 리스트를 사용하는 방법의 단점은 병렬화하기가 어렵다는 것이다. 새로운 리스트를 생성하는 것이 더 좋은 방법이다.
- 함수형 접근 방법은 명령형 접근 방법보다 더 좋은 성능을 얻곤한다.

리스트의 내용을 대문자로 바꾸기
- 명령형 코딩 : 빈 리스트를 생성한 후에, 원본 리스트를 이터레이션하면서 리스트의 각각의 엘리먼트에 대해 한 번에 하나씩 대문자로 된 이름을 채워 넣는다.
- for문 사용하기
- forEach문 사용하기

람다 표현식 사용
- 스트림 인터페이스의 map() 메서드 사용하면, 가변성이 발생하지 않도록 할 수 있다.
- 스트림은 객체의 컬렉션에 있는 이터레이터와 매우 유사하며 다양한 기능의 함수를 제공한다.
- 이 인터페이스의 메서드를 사용하면 호출 순서를 조합할 수 있고, 해결해야 하는 문제를 설명하는 것과 같은 방식으로 코드를 구성할 수 있다.
- 스트림의 map() 메서드는 입력 순서를 출력 순서로 매핑하거나 입력 순서를 다른 순서의 출력으로 변형한다.

stream() 메서드는 모든 컬렉션에서 사용할 수 있으며 스트림 인스턴스에 대한 컬렉션을 래핑한다.
map() 메서드는 주어진 람다 표현식이나 코드 블록에 적용되며 괄호로 구성되어 있고, 스트림의 각 엘리먼트를 처리한다.
map() 메서드는 forEach() 메서드와는 전혀 다르다.
컬렉션에 있는 각 엘리먼트의 내용에 대한 처리를 한다. 게다가 map() 메서드는 람다 표현식의 실행 결과를 취합하여 결과 컬렉션으로 리턴한다.
마지막으로 이 결과물의 엘리먼트를 forEach() 메서드를 사용하여 출력한다.

map() 메서드는 입력 컬렉션을 출력 컬렉션으로 매핑하거나 변경하는 데 꽤 유용하다.
이 메서드는 입력 엘리먼트의 수와 결과 엘리먼트의 수가 동일하다는 것을 보장한다.
그러나, 입력 엘리먼트에 대한 타입은 출력 컬렉션의 엘리먼트 타입과 같을 필요는 없다.
코드 블록을 map() 메서드에 파라미터로 넘길 수 있다.
다음 예제의 입력은 스트링이지만, 출력은 숫자의 집합이 된다.

람다 표현식 버전은 명시적 변경이 없으며 간결하다.
더 이상 비어 있는 컬렉션의 초기화 작업이나 가비지 변수가 필요하지 않다.
이러한 작업에 필요한 변수들은 내부 라이브러리에 맡겨두면 된다.

메서드 레퍼런스라는 기능을 사용하여 코드를 좀 더 간결하게 만들 수 있다.
자바 컴파일러는 람다 표현식 뿐만 아니라 함수형 인터페이스의 구현이 필요한 코드에 대해 메서드의 레퍼런스를 사용할 수 있다.
name -> name.toUpperCase() ⇒ String::toUpperCase 로 변경하능
자바는 String 클래스에서 제공하는 메서드인 toUpperCase()를 호출한다.
이 메서드는 합성된 메서드에 전달된 파라미터이며 함수형 인터페이스의 추상 메서드구현에 해당된다.
String::toUpperCase라는 파라미터 레퍼런스가 암묵적으로 제공된다.
메서드 레퍼런스는 인스턴스 메서드를 위해서 사용된다.
메서드 레퍼런스는 또한 정적 메서드를 참조하며 파라미터를 갖는다.

람다 표현식을 사용하면 컬렉션을 나열하고 새로운 컬렉션으로 변경할 수 있다.
또한 컬렉션에서 하나의 엘리먼트를 선택하는 기능도 간단하게 할 수 있다.

메서드 레퍼런스를 언제 사용해야 하는가?
일반적으로 메서드 레퍼런스보다는 람다 표현식을 더 많이 사용한다.
메서드 레퍼런스는 람다 표현식이 짧아서 간단하게 만들거나 인스턴스 메서드 혹은 정적 메서드를 직접 호출하는 경우에 유용하다.
다시 말하면, 람다 표현식을 사용할 때 파라미터를 전달하지 않는 경우라면 메서드 레퍼런스를 사용할 수 있다는 의미이다.
메서드 레퍼런스를 사용하면 이미 선택한 메서드의 이름을 직접 사용할 수 있는 이점이 있다.
메서드 레퍼런스를 사용할 때 내부적으로 약간의 컴파일러의 도움이 필요하다.
메서드 레퍼런스의 타깃 객체와 파라미터는 합성된 메서드에 전달된 파라미터로부터 파생됐다.
파라미터를 인수로 전달하기 전에 파라미터를 처리해야 하거나 혹은 리턴하기 전에 호출의 결과를 사용해야 하는 경우에는 사용할 수 없다.

엘리먼트 찾기
- filter() 메서드는 boolean 결과를 리턴하는 람다 표현식이 필요하다
- 람다 표현식이 true를 리턴하면 람다 표현식이 실행하는 동안 컨텍스트의 엘리먼트는 결과 컬렉션에 추가된다.
- 마지막으로 메서드에서 람다 표현식이 true라고 체크한 엘리먼트의 스트림을 리턴한다.
- 결과를 collect()메서드를 사용하여 리스트로 변경한다
- map() 메서드가 입력과 같은 크기의 컬렉션을 리턴하는 반면에 filter() 메서드는 그렇지 않다.

람다 표현식의 재사용성
- 람다 표현식을 변수에 저장해서 다시 사용할 수 있다.
- 이 방법은 객체와 비슷한 개념이다

렉시컬 스코프와 클로저 사용하기
- 몇몇 개바자 사이에는 람다 표현식을 사용하는 것이 코드를 중복 사용하게 하며 코드 품질을 떨어뜨린다는 잘못된 생각을 하고 있기도 하다

엘리먼트 선택
- Optional 클래스는 결과가 없는 경우에 유용하다
- isPresent() 메서드
- orElse() 메서드

컬렉션을 하나의 값으로 리듀스
- 컬렉션을 다루는 여러 가지 기술들의 공통점 : 컬렉션에 있는 각각의 엘리먼트에 대해 독립적으로 동작한다.
- 맵 리듀스 패턴
- reduce() 메서드를 이용하여 두 개의 엘리먼트를 서로 비교하고 컬렉션에 남아 있는 엘리먼트와의 비교를 통해 결과를 얻어낸다.
- reduce() 메서드의 결과는 Optional이며 그 이유는 reduce()가 호출된 리스트가 비워져 있을 수도 있기 때문이다

엘리먼트 조인
- collect() 메서드



chapter 3 String, Comparator, 그리고 filter

스트링 이터레이션
- chars() 메서드

Comparator 인터페이스의 구현
- sorted() 메서드
- comparedTo() 메서드
-

여러가지 비교 연산
- 람다 표현식의 편의성과 JDK의 새로운 utility 클래스를 조합하여 사용하면 Comparator를 쉽게 구현할 수 있다.
- comparing() 메서드

collect 메서드와 Collectors 클래스 사용하기
- collect() 메서드 : 가변 컬렉션으로 변경하는데 유용한 리듀스 오퍼레이션이다

Collector는 supplier, accumulator, combiner의 오퍼레이션에 대한 인터페이스 역할을 한다.
- toList(), toSet(), toMap() 메서드
- joining() 메서드
- mapping(), collectingAndThen(), minBy(), maxBy(), groupingBy() 메서드

디렉터리에서 모든 파일 리스트하기
- Paths 클래스

디렉터리에서 선택한 파일 리스트하기
- FilenameFilter 인터페이스

flatMap을 사용하여 서브 디렉터리 리스트하기

파일 변경 살펴보기
- 와치 서비스



chapter 4 람다 표현식을 이용한 설계

OOP는 사실상 표준이지만 자바에서 람다 표현식을 사용하면서 설계 방법에 더 많은 기술적 진보를 이루었다.
자바에서 OOP와 함수형 스타일은 서로 보완관계이며 함께 사용했을 때 상당한 효과를 볼 수 있다.
수정과 확장이 쉽고 유연하며 효율적인 설계를 할 수 있다.

람다를 사용한 문제의 분리
- 람다 표현식을 사용하여 스트레티지 패턴 이해하기

람다 표현식을 사용하여 델리게이트하기
- 재사용 측면에서 보면 델리게이트는 상속보다 더 좋은 설계 도구이다.

람다 표현식을 사용한 데코레이팅

디폴트 메서드 들여다보기

람다 표현식을 사용하여 인터페이스를 풍부하게 만들기
- 메서드 체인 사용하기


예외 처리 다루기



chapter 5 리소스를 사용한 작업

외부 리소스를 사용한다면 리소스에 대한 가비지 컬렉션은 프로그래머의 책임이다.
- JVM에서는 외부 리소스에 대해서는 자동으로 가비지 컬렉션을 하지 않는다.

리소스 클린업


리소스를 클린업하기 위해 람다 표현식 사용하기


잠금(lock) 관리
- synchronized는 상호 배재를 제공하기 위해 사용되어온 오래된 키워드다
- Lock 인터페이스

간결한 예외 테스트의 생성



chapter 6 레이지

코드에서 호출되는 순간에 해당 코드에서 사용하는 변수와 문장이 실행되도록 하는 것을 이거 코드(eager code)라고 하며, 마지막 책임질 순간까지 코드의 실행에 대한 커밋을 지연시키는 방법은 애자일(Agile) 실천에 대한 좋은 예이다.
- 이거(eager) 방식은 간단하지만, 레이지(lazy) 방식은 효율적이다.

지연 초기화
- 객체 지향 프로그래밍에서 객체는 메서드가 호출되기 전에 생성된다.


레이지 이밸류에이션
- 실행하는 메서드를 지연시켜 설계를 향상해보자


스트림의 레이지 강화하기
- 스트림에 대한 레이지 솔루션은 상당히 강력하다


무한, 그리고 레이지 컬렉션의 생성
- 무한 컬렉션은 점점 증가하는 혹은 자라나는 연속된 데이터들을 처리하는 코드를 생성하는 데 사용된다.
- 자바에서 컬렉션은 유한하지만 스트림은 무한하다



chapter 7 재귀 호출 최적화

재귀는 문제를 해결하기 위한 강력하면서도 매혹적인 방법이다.
- 이 방법은 문제를 서술적으로 기술하여 해결할 수 있다.
- 서술적으로 기술한다는 의미는 재귀를 사용하여 주어진 하나의 문제를 여러 개의 서브 문제로 나누어 해결한다는 의미다.
- 이 방법은 이미 잘 알려진 분할 그리고 정복(Divide and Conquer)이다.


테일-콜 최적화 사용
- 재귀를 사용할 때의 가장 큰 문제점은 입력 데이터가 매운 많은 경우에 스택 오버플로우가 발생할 위험이 있다는 것이다.
- 테일-콜은 마지막 오퍼레이션이 실행되는 위치에 있는 재귀 호출이 자기 자신을 호출하는 방법을 말한다.


메모이제이션으로 성능 향상
- 재귀 방법을 사용하는 문제를 해결하기 위한 코드를 빠르게 실행할 수 있도록 해주는 기술
- 다이내믹 프로그래밍



chapter 8 람다 표현식의 조합

자바 8을 사용하면 두 가지 강력한 도구를 갖게 된다. 첫 번째는 객체 지향이고 두번째는 함수형 스타일이다.


함수 조합의 사용


맵리듀스의 사용
- 맵리듀스 패턴에서는 두 가지의 오퍼레이션을 사용한다.
- 하나는 컬렉션에 있는 각 엘리먼트에 대해 실행하는 오퍼레이션이며 다른 하나는 최종 결과를 위해 기존의 결과를 조합하는 오퍼레이션이다.


병렬화 적용



chapter 9 모든 것을 함께 사용해보자

함수형 스타일을 업무에 성공적으로 적용하기
- 자바 8의 새로운 기능은 단지 문법을 변경한 것이 아니다.
- 더 간결하고 경량화된 애플리케이션을 개발하고 이를 통해 더욱 많은 장점을 얻기 위해서는 설계, 코드, 그리고 우리의 생각까지도 변화해야 한다.


성능 문제


함수형 스타일의 적용



Appendix A 함수형 인터페이스의 집합

java.util.function 패키지



Appendix B 신택스 오버뷰

함수형 인터페이스의 정의

파라미터가 없는 람다 표현식의 생성

하나의 파라미터를 갖는 람다 표현식

람다 표현식의 파라미터 타입 추론

한 개의 파라미터만을 추론하는 경우에 괄호의 생략

다중 파라미터 람다 표현식의 생성

혼합된 파라미터를 사용한 메서드의 호출

람다 표현식의 저장

다중 라인 람다 표현식의 생성

람다 표현식의 리턴

람다 표현식에서 람다 표현식의 리턴

클로저에서 렉시컬 스코프

인스턴스 메서드의 메서드 레퍼런스 전달하기

정적 메서드에서 메서드 레퍼런스를 전달하기

메서드 레퍼런스를 다른 인스턴스의 메서드로 전달하기

파라미터를 가진 메서드의 레퍼런스 전달하기

생성자 레퍼런스의 사용

함수 조합



Appendix C Web Resources