본문은 Effective Java를 읽고 간단하게 정리한 글입니다. 필요에 따라 생략/수정된 부분이 있을 수 있으며, 내용이 추후 변경될 수 있습니다.
선결론
- 메서드 참조는 람다의 간단명료한 대안이 될 수 있다
- 메서드 참조 쪽이 짧고 명확하다면 메서드 참조를 쓰고, 그렇지 않을 때만 람다를 사용하라
메서드 참조
- 람다는 익명 클래스보다 간결하다
- 메서드 참조(method reference)를 이용하면 람다보다도 간결하게 함수 객체를 만들 수 있다
다음은 메서드 참조를 이용하여 코드를 간결하게 만드는 예시이다
map.merge(key, 1, (count, incr) -> count + incr);
- merge 메서드는 키, 값, 함수를 받는다
- 주어진 키가 맵 안에 없다면 주어진 키, 값 쌍을 그대로 저장한다
- 키가 맵 안에 이미 있다면 함수를 현재 값과 주어진 값에 적용한 다음, 그 결과로 현재 값을 덮어쓴다
- 매개변수인 count와 incr은 불필요하게 공간을 차지한다
map.merge(key, 1, Integer::sum);
- 자바 8부터 Integer 클래스는 위 코드의 람다와 기능이 같은 정적 메서드 sum을 제공하기 시작했다
- 람다 대신 이 메서드의 참조를 전달하면 똑같은 결과를 더 보기 좋게 얻을 수 있다
메서드 참조 vs 람다
- 매개변수의 수가 늘어날수록 메서드 참조로 제거할 수 있는 코드양도 늘어난다
- 하지만 어떤 람다에서는 매개변수의 이름 자체가 프로그래머에게 좋은 가이드가 되기도 한다
- 이런 람다는 길이는 더 길지만 메서드 참조보다 읽기 쉽고 유지보수도 쉬울 수 있다
- 람다로 할 수 없는 일이라면 메서드 참조로도 할 수 없다
- 예외가 하나 있는데, 함수형 인터페이스를 위한 제네릭 함수 타입은 메서드 참조 표현식으로는 구현할 수 있지만 람다식으로는 불가능하다
- 대체로 메서드 참조를 사용하는 편이 더 짧고 간결하므로 람다로 구현했을 때 너무 길거나 복잡하다면 메서드 참조가 좋은 대안이 된다
- 즉 람다로 작성할 코드를 새로운 메서드에 담은 다음, 람다 대신 그 메서드 참조를 사용하는 식이다
- 메서드 참조에는 기능을 잘 드러내는 이름을 지어줄 수 있고, 문서화시킬 수도 있다
- IDE들은 람다를 메서드 참조로 대체하라고 권하는데, 이를 따르는 게 보통은 이득이지만, 때로는 람다가 메서드 참조보다 간결할 때가 있다
- 주로 메서드와 람다가 같은 클래스 안에 있을 때 그러하다
예를 들어 다음 코드가 GoshThisClassNameIsHumongous 클래스 안에 있다고 가정해보자
service.excute(GoshThisClassNameIsHumongous::action);
이를 람다로 대체하면 다음처럼 된다
service.execute(() -> action());
- 이 경우 메서드 참조보다 람다를 직접 작성한 편이 더 간결하고 명확하다
- 같은 맥락에서 java.util.function 패키지가 제공하는 제네릭 정적 팩터리 메서드인 Function.identity()를 사용하기보다는 똑같은 기능의 람다(x -> x)를 사용하는 편이 짧고 명확하다
메서드 참조의 유형 5가지
1. 정적 메서드를 가리키는 메서드 참조
- 위의 예제들과 동일하다
2. 수신 객체(reveiving object; 참조 대상 인스턴스)를 특정하는 한정적(bound) 인스턴스 메서드 참조
- 정적 참조와 유사
- 이미 존재하는 외부 개체에 대해 람다의 메서드를 참조하는 상황
- 함수 객체가 받는 인수와 참조되는 메서드가 받는 인수가 동일하다
3. 수신 객체를 특정하지 않는 비한정적(unbound) 인스턴스 메서드 참조
- 람다식의 매개변수로 제공되는 객체의 메서드를 참조하는 상황
- 함수 객체를 적용하는 시점에 수신 객체를 알려준다
- 주로 스트림 파이프라인에서의 매핑과 필터 함수에 쓰인다
4. 클래스 생성자를 가리키는 메서드 참조
5. 배열 생성자를 가리키는 메서드 참조
- 생성자 참조는 팩터리 객체로 사용된다
메서드 참조 유형 | 예 | 같은 기능을 하는 람다 |
정적 | Integer::parseInt | str -> Integer.parseInt(str) |
한정적(인스턴스) | Instant.now()::isAfter | Instant then = Instant.now(); t -> then.isAfter(t) |
비한정적(인스턴스) | String::toLowerCase | str -> str.toLowerCase() |
클래스 생성자 | TreeMap<K,V>::new | () -> new TreeMap<K,V>() |
배열 생성자 | int[]::new | len -> new int[len] |
'책 > Effective Java' 카테고리의 다른 글
[이펙티브 자바] 아이템 48: 스트림 병렬화는 주의해서 적용하라 (0) | 2022.06.17 |
---|---|
[이펙티브 자바] 아이템 46: 스트림에서는 부작용 없는 함수를 사용하라 (0) | 2022.06.15 |
[이펙티브 자바] 아이템 42: 익명 클래스보다는 람다를 사용하라 (0) | 2022.06.10 |
[이펙티브 자바] 아이템 38: 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라 (0) | 2022.06.03 |
[이펙티브 자바] 아이템 32: 제네릭과 가변인수를 함께 쓸 때는 신중하라 (0) | 2022.05.24 |