Technology/Effective Java 3E

Item 43: 람다보다는 메서드 참조를 사용하라

hanulpark 2022. 1. 21. 22:54
728x90
반응형

람다가 익명 클래스보다 나은 점 중에서 가장 큰 특징은 간결함이다.

그런데 자바에는 함수 객체를 심지어 람다보다도 더 간결하게 만드는 방법이 있으니, 바로 메서드 참조(method reference).

 

다음 코드는 임의의 키와 Integer 값의 매핑을 관리하는 프로그램의 일부다.

이때 값이 키의 인스턴스 개수로 해석 된다면, 이 프로그램은 멀티셋(multiset)을 구현한 게 된다.

 

이 코드는 키가 맵 안에 없다면 키와 숫자 1을 매핑하고, 이미 있다면 기존 매핑 값을 증가시킨다.

// 람다 방식
map.merge(key, 1, (count, incr) -> count + incr);

 

이 코드는 자바 8  Map에 추가된 merge 메서드를 사용했다.

merge 메서드는 키, , 함수를 인수로 받으며, 주어진 키가 맵 안에 아직 없다면 주어진 (, ) 쌍을 그대로 저장한다.

 

반대로 키가 이미 있다면 (세 번째 인수로 받은) 함수를 현재 값과 주어진 값에 적용한 다음, 그 결과로 현재 값을 덮어쓴다.

코드는 merge 메서드의 전형적인 쓰임을 잘 보여주고 있다.

 

사실 이 람다는 두 인수의 합을 단순히 반환할 뿐이다.

 

자바 8되면서 Integer 클래스(모든 기본 타입의 박싱 타입)정적 메서드 sum제공하기 시작했다.

람다 대신 이 메서드의 참조를 전달하면 똑같은 결과를 더 보기 좋게 얻을 있다.

// 메서드 참조 방식
map.merge(key, 1, Integer::sum);

 

어떤 람다에서는 매개변수의 이름 자체가 프로그래머에게 좋은 가이드가 되기도 한다.

람다는 길이는 더 길지만 메서드 참조보다 읽기 쉽고 유지보수도 쉬울 있다.

 

그렇더라도 메서드 참조를 사용하는 편이 보통은 짧고 간결하므로, 람다로 구현했을 때 너무 길거나 복잡하다면 메서드 참조가 좋은 대안이 되어준다.

 

, 람다로 작성할 코드를 새로운 메서드에 담은 다음, 람다 대신 그 메서드 참조를 사용 하는 식이다.

메서드 참조에는 기능을 잘 드러내는 이름을 지어줄 있고 친절한 설명을 문서로 남길 수도 있다.

 

주로 메서드와 람다가 같은 클래스에 있을 때, 람다가 메서드참조 보다 간결하.

예를 들어 다음 코드가 GoshThisClassNamelsHumongous 클래스 안에 있다고 해 보자.

service.execute(GoshThisClassNamelsHumongous::action);

 

이를 람다로 대체하면 다음 처된다.

service.execute(() -> action());

 

또 다른 예로 java.util.function 패키지가 제공하는 제네릭 정적 팩터리 메서드인 Function.identity()를 사용하기보다는 똑같은 기능의 람다(x -> x)를 직접 사용하는 편이 코드도 짧고 명확하다.

 

메서드 참조의 유형 5가지

메서드 참조 유형
같은 기능을 하는 람다
정적
Integer::parselnt
str -> Integer.parselnt(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]

가장 흔한 유형은 정적 메서드를 가리키는 메서드 참조다.

 

인스턴스 메서드를 참조하는 2가지 유형

  1. 수신 객체(receiving object; 참조 대상 인스턴스)를 특정하는 한정적(bound) 인스턴스 메서드 참조

    한정적 참조는 근본적으로 정적 참조와 비슷하다.
    , 함수 객체가 받는 인수와 참조되는 메서드가 받는 인수가 똑같다.

  2. 수신 객체를 특정하지 않는 비한정적(unbound) 인스턴스 메서드 참조

    비한정적 참조에서는 함수 객체를 적용하는 시점에 수신 객체를 알려준다
    .

    이를 위해 수신 객체 전달용 매개변수가 매개변수 목록의 첫 번째로 추가되며
    , 그 뒤로는 참조되는 메서드 선언에 정의된 매개변수들이 뒤따른다.

    비한정적 참조는 주로 스트림 파이프라인에서의 매핑과 필터 함수에 쓰인다(아이템 45).

 

클래스 생성자를 가리키는 메서드 참조와 배열 생성자를 가리 키는 메서드 참조가 있다.

생성자 참조는 팩터객체로 사용된다.

 

람다로는 불가능하나 메서드 참조로는 가능한 유일한 예는 바로 제네릭 함수 타입(generic function type) 구현이다.

참고: Example 9.9-2. Generic Function Types

https://bit.ly/2uYQnbh

 

Chapter 9. Interfaces

If a single field is inherited multiple times from the same interface because, for example, both this interface and one of this interface's direct superinterfaces extend the interface that declares the field, then only a single member results. This situati

docs.oracle.com

 

이처럼 함수형 인터페이스를 위한 제네릭 함수 타입은 메서드 참조 표현식으로는 구현할 수 있지만, 람다식으로는 불가능하다.

제네릭 람다식이라는 문법이 존재하지 않기 때문이다.

 

핵심 정리

메서드 참조는 람다의 간단명료한 대안이 될 수 있다.

메서드 참조 쪽이 짧고 명확하다면 메서드 참조를 쓰고그렇지 않을 때만 람다를 사용하라.

728x90
반응형