> For the complete documentation index, see [llms.txt](https://incheol-jung.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://incheol-jung.gitbook.io/docs/study/functional-programming-in-java-8/2..md).

# 부록 2. 신택스 오버뷰

이 책에서 사용했던 함수형 인터페이스, 람다 표현식, 메서드 레퍼런스, 그리고 생성자 레퍼런스에 대한 새로운 신택스에 대해 알아보자

### 함수형 인터페이스의 정의

```java
@FunctionalInterface
public interface TailCall<T> {
		TailCall<T> apply();
		default boolean isComplete() { return false; }
}
```

함수형 인터페이스는 아직 구현하지는 않은 한 개의 추상 메서드를 가져야 한다. 디폴트 혹은 구현 메서드가 아예 없을 수도 있고 여러 개 있을 수 있다. 또한 정적 메서드를 가질 수도 있다.

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

```java
lazyEvaluator(() -> evaluate(1), () -> evaluate(2));
```

비어있는 파라미터 리스트를 둘러싸고 있는 괄호 ()는 람다 표현식이 아무 파라미터도 갖지 않더라도 필요하다. → 기호는 람다 표현식의 본문과 파라미터를 분리한다.

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

```java
friends.forEach((final String name) -> System.out.println(name));
```

자바 컴파일러는 컨텍스트에 따라 람다 표현식의 타입을 추론한다. 추론할 타입이 적절하지 않거나 좀 더 명확하게 하고 싶은 경우에는 파라미터 이름 앞에 타입을 설정해준다.

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

```java
friends.forEach((name) -> System.out.println(name));
```

자바 컴파일러는 프로그래머가 타입을 제공하지 않으면 파라미터의 타입을 추론하려고 시도한다. 파라미터를 추론하는 것은 발생할 수도 있는 오류를 미연에 방지하고, 프로그래머가 일일이 파라미터의 타입을 정해주지 않아도 된다. 그러나 한 개의 파라미터의 타입이라도 설정한다면 람다 표현식에 있는 모든 파라미터의 타입을 설정해줘야 한다.

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

```java
friends.forEach(name -> System.out.println(name));
```

람다 표현식이 하나의 파라미터만 갖고 있고 이 파라미터를 추론하는 경우라면 파라미터를 둘러싸고 있는 괄호 ()는 옵션이다. 즉, 괄호를 생략해도 된다. 따라서 name → ... 혹은 (name) → ... 어느 것을 사용해도 무방하나, 첫 번째 경우처럼 괄호를 생략하는 것이 좀 더 간단하다.

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

```java
friends.stream()
				.reduce((name1, name2) ->
					name1.length() >= name2.length() ? name1 : name2);
```

파라미터 리스트를 둘러싸고 있는 괄로 ()는 람다 표현식이 여러 개의 파라미터를 갖거나 아예 없는 경우에는 괄호를 생략하면 안 된다.

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

```java
friends.stream()
				.reduce("Steve", (name1, name2) ->
					name1.length() >= name2.length() ? name1 : name2);
```

메서드에서 일반적인 클래스, 원시 타입, 함수형 인터페이스가 파라미터 형태로 섞여 있다. 메서드의 파라미터는 함수형 인터페이스가 될 수 있기 때문에 람다 표현식이나 메서드 레퍼런스를 함수형 인터페이스의 위치에 인수로서 전달한다.

### 람다 표현식의 저장

```java
final Predicate<String> startWithN = name -> name.startWith("N");
```

재사용의 목적이거나 중복을 피하기 위해서는 변수에 람다 표현식을 저장하기도 한다.

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

```java
FileWriterEAM.use("eam2.txt", writerEAM -> {
		writerEAM.writeStuff("how");
		writerEAM.writeStuff("how");
});
```

람다 표현식은 되도록 짧게 유지하는 것이 좋지만 여러 라인에 걸쳐서 표현하는 것이 이해하기 쉬울 때도 있다. 그런 경우에는 중괄호 { }를 사용하며 람다 표현식이 어떤 값을 리턴하는 경우라면 return 키워드를 사용한다.

### 람다 표현식의 리턴

```java
public static Predicate<String> checkIfStartsWith(final String letter) {
		return name -> name.startsWith(letter);
}
```

메서드의 리턴 타입이 함수형 인터페이스라면, 메서드의 구현 부분 안에서 람다 표현식을 리턴한다.

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

```java
final Function<String, Predicate<String>> startWithLetter =
		letter -> name -> name.startWith(letter);
```

람다 표현식 자체가 람다 표현식을 리턴하는 람다 표현식을 생성할 수도 있다. 위의 코드에서 function 인터페이스의 구현 부분은 스트링 letter를 받아서 Predicate 인터페이스를 따르는 람다 표현식을 리턴한다.

### 클로저에서 렉시컬 스코프

```java
public static Predicate<String> checkIfStartWith(final String letter) {
		return name -> name.startsWith(letter);
}
```

람다 표현식에서 메서드 스코프 내에 있는 변수는 엑세스할 수 있다. 예를 들어 checkIfStartWith() 안에 있는 변수 letter는 람다 표현식 안에서만 엑세스가 가능하다. 제한적인 스코프 안에서 변수에 대한 엑세스를 허용하는 람다 표현식을 클로저라고 한다.

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

```java
friends.stream()
				.map(String::tuUpperCase);
```

람다 표현식을 사용할 때 타깃의 파라미터로 간단한 메서드 호출을 직접 사용하는 경우라면 람다 표현식 대신 메서드 레퍼런스로 대체할 수 있다.

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

```java
str.chars()
		.filter(Character::isDigit);
```

람다 표현식을 사용할 때 파라미터로 직접 정적 메서드를 사용하는 경우라면 람다 표현식을 메서드 레퍼런스로 대체할 수 있다.

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

```java
str.chars()
		.forEach(System.out::println);
```

람다 표현식을 사용할 때 파라미터로 다른 인스턴스의 메서드를 사용하는 경우라면 람다 표현식을 메서드 레퍼런스로 대체할 수 있다. 예를 들면, System.out에서 println()과 같은 경우다.

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

```java
people.stream()
				.sorted(Person::ageDifference)
```

람다 표현식을 사용할 때 첫 번째 파라미터로 메서드 호출이 그 타깃이라면 람다 표현식을 메서드 레퍼런스로 대체할 수 있다. 그리고 나머지 파라미터는 메서드의 인수가 된다.

### 생성자 레퍼런스의 사용

```java
Suppplier<Heavy> supplier = Heavy::new;
```

생성자를 호출하는 대신 자바 컴파일러로 하여금 간결한 생성자-레처런스 신택스에 따라 적절한 생성자에 대한 호출을 만들도록 하게 할 수 있다. 이러한 작업은 메서드 레퍼런스와 상당시 비슷하지만, 단지 생성자를 참조하고 이 작업의 결과가 객체의 초기화라는 점이 다르다.

### 함수 조합

```java
symbols.map(StockUtil::getPrice)
				.filter(StockUtil::isPriceLessThan(500))
				.reduce(StockUtil::pickHigh)
				.get();
```

위의 예제와 같이 여러 개의 오퍼레이션을 차례로 실행하여 객체를 변경하도록 함수들을 조합하여 사용할 수 있다. 함수형 스타일 프로그래밍에서는 함수 조합이나 체인은 결합 오퍼레이션을 구현하기 위한 매우 강력한 도구이다.
