# 43. 람다보다는 메서드 참조를 사용하라

> 가능한 람다를 사용하자!

## 📌 1. 발표 전 알아야 할 개념

✅ 수신 객체(receiver object)

* 메서드가 호출되는 대상 객체
* `kim.toUpperCase()` 라고 하면 -> 수신 객체는 `kim`

✅ 람다에 대해 다시 한 번 짚고 넘어가자!

* 람다 : java 8 이후에 도입된, 익명 함수를 간단하게 표현하는 방식

```java
// Consumer<T> 람다 구현
List<String> carList = List.of("Tesla", "GV70", "Porshce");
carList.forEach(car -> System.out.println(car));
```

<br>

## 📕 2. 메서드 참조를 사용하라

### 2-1. 메서드 참조란?

* 기존의 메서드 이름을 그대로 사용하는 표현 방식
* 간결하고, 의도를 명확하게 표현할 수 있다

위의 식을 바꿔보면

```java
carList.forEach(System.out::println);
// System.out 객체의 println 메서드를 각각의 요소에 대해 호출하라
```

(예시)

```java
// compareToIgnoreCase 구현(대소문자를 무시하고 비교)
// 람다 표현식
list.sort((s1, s2) -> s1.compareToIgnoreCase(s2));

// 메서드 참조
list.sort(String::compareToIgnoreCase);
```

### 2-2. 메서드 참조의 5가지 종류

1. 정적 메서드를 가리키는 메서드 참조

* `ClassName::staticMethod` 형식
* 정적 메서드를 참조함
* ex) Math::abs, Integer::parseInt

```java
Function<String, Integer> parse = Integer::parseInt;
System.out.println(parse.apply("123"));
```

2. 수신 객체를 특정하는 한정적 인스턴스 메서드 참조

* `instance::instanceMethod` 형식
* 수신 객체가 이미 정해져 있음
* 인스턴스를 대상으로 메서드 호출
* ex) System.out::println

```java
PrintStream out = System.out;
Consumer<String> printer = out::println;
```

3. 수신 객체를 특정하지 않는 비한정적 인스턴스 메서드 호출

* `ClassName::instanceMethod` 형식
* 수신 객체가 지금은 정해져 있지 않지만, 실행시 첫 번째 인자가 수신 객체
* 클래스 타입 기준으로 메서드를 참조
* ex) String::compareToIgnoreCase, String::equalsIgnoreCaes

```java
List<String> names = List.of("Kim", "Lee", "Park");
names.sort(String::compareToIgnoreCase); // 각 요소끼리 비교
```

4. 클래스 생성자를 가리키는 메서드 참조

* `ClassName::new` 형식
* 생성자를 참조
* ex) ArrayList::new, HashMap::new

```java
Supplier<List<String>> listSupplier = ArrayList::new;
List<String> list = listSupplier.get(); // 새 ArrayList 생성
```

5. 배열 생성자를 가리키는 메서드 참조

* `Type[]::new` 형식
* 배열을 생성하는 생성자 참조
* ex) String\[]::new, int\[]::new

```java
IntFunction<String[]> arrayCreator = String[]::new;
String[] arr = arrayCreator.apply(5);
```

## 🤔 3. 메서드 참조의 장단점

### 3-1. 왜 메서드 참조를 선호해야 할까?

* 불필요한 매개변수, 람다 본문 생략으로 간결함
* 메서드를 그대로 사용하기 때문에 의도가 더 명확하게 드러난다
* 중복을 줄일 수 있다

### 3-2. 메서드 참조가 안 되는 경우?

* 단순한 메서드 호출이 아닌 경우
* 조건문이나 루프를 포함하는 경우
* 변수 캡쳐를 포함하는 경우

예시)

```java
list.forEach(name -> {
    log(name);
    sendEmail(name);
})
```

메서드 참조로 바꾸기 위해서는 람다 본문을 별도의 메서드로 추출하고, 메서드를 메서드 참조로 넘겨야 한다.

람다식을 process라는 메서드로 추출하고,

```java
public class Service {
    public static void process(String name) {
        log(name);
        sendEmail(name);
    }

    private static void log(String name) {
        System.out.println("Logging: " + name);
    }

    private static void sendEmail(String name) {
        System.out.println("Sending email to: " + name);
    }
}
```

메서드 참조로 바꾸면 된다!

```java
list.forEach(Service::process);
```

***

## 💨 향후 확장 포인트

* 람다로 할 수 없는 일이라면 메서드 참조로도 할 수 없다(애매한 예외 단 한가지를 빼고...)
* 람다로 표현할 때 타입 인자를 일반적으로 생략해야하는데, 제네릭의 경우 타입을 직접 지정해야 한다. 다만 메서드 참조의 경우 문맥에서 타입을 추론할 수 있으므로 훨씬 깔끔함

```java
public class MyUtils {
    public static <T> T identity(T t) {
        return t;
    }
}
```

```java
// 타임 명시를 안할 경우 - x의 타입이 확실하지 않음
UnaryOperator<String> identity1 = x -> MyUtils.identity(x);
// 람다 - 타입 명시를 해야함 <String>
UnaryOperator<String> identity1 = x -> MyUtils.<String>identity(x);
// 메서드 참조 - 타입 추론 가능!
UnaryOperator<String> identity2 = MyUtils::identity;
```

***

## 🤖 최종 결론

> 메서드 참조가 짧고 명확하다면 메서드 참조를 써라!

***

## ❗어려웠던 점

* 이전의 학습 내용을 계속 확장하면서 공부해서 이해가 안되는 부분이 있거나 헷갈리는 부분에서 이해가 바로 안됐지만, 오히려 한 번 더 정리할 수 있었다!
* 제네릭의 경우 메서드 참조가 유리하다는 점에서 컴파일의 해석 방식을 생각해야 하는 부분이 두개가 비슷하게 느껴져 어려웠다.

***

## 😶‍🌫️ 느낀점

* 단순히 짧게 쓰는 문법이 아니라 의도가 더 명확하게 드러나고, 가독성과 의미 전달에서 메서드 참조를 사용해야겠다고 생각했다!
