X Tutup
Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

README.md

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

책정리

아이템45

메서드 참조

// 이 코드는 키가 맵 안에 없다면 키와 숫자 1을 매핑, 이미 있다면 기존 매핑 값을 증가시킨다.
map.merge(key, 1, (count, incr) -> count + incr);

이 코드는 Map에 추가된 merge 메서드를 사용했다. 람다식을 사용하여 맵에 {키, 함수의 결과} 쌍을 저장한다.

아쉬운 점은 매개변수인 count, incr은 크게 하는 일 없이 공간을 차지한다. 자바8이 되면서 Integer 클래스(박싱타입)는 람다와 기능이 같은 정적 메서드 sum을 제공하기 시작했다.

다음처럼 메서드 참조를 사용하면 더욱 간단해진다.

map.merge(key, 1, Integer::sum);

매개변수의 수가 늘어날수록 메서드 참조로 제거할 수 있는 코드양도 늘어난다.

람다와 메서드 참조

람다로 할 수 없는 일이라면 메서드 참조로도 할 수 없다.

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

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

메서드 참조의 예외

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

// 메서드 참조
service.execute(GoshThisClassNameIsHumongous::action);
// 람다(더욱 간결)
service.execute(() -> action());

메서드 참조의 유형

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

Integer::parseInt
// lambda
str -> Integer.parseInt(str)

2. 인스턴스 메서드 참조

2-1. 수신 객체(receive object: 참조 대상 인스턴스)를 특정하는 한정적 인스턴스 메서드 참조

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

Instant.now()::isAfter
// lambda
Instant then = Instant.now();
t -> then.isAfter(t)

2-2. 비한정적 인스턴스 메서드 참조

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

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

String::toLowerCase
// lambda
str -> str.toLowerCase()

3. 클래스 생성자

TreeMap<K,V>::new
// lambda
() -> new TreeMap<K,V>()

4. 배열 생성자

int[]::new
// lambda
len -> new int[len]

Q&A

Q1.

p260

인스턴스 메서드를 참조하는 두가지 유형.. 한정적, 비한정적

  • 이해될듯 안될듯 어렴풋한 느낌입니다.
  • 한정적은 해당 기능을 수행하기 위해서는 특정 인스턴스가 꼭 필요해서 한정적이라 한 것 같은데,
  • 비한정적인 경우는 어떤 걸까요?

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

  • 이 부분이 잘 이해가 안되서 그런지 비한정적이라 명명한 이유를 잘 모르겠네요.
  • 한번 이야기 나눠보면 좋을 것 같아요

A1.

비한정적 메서드 참조

public 또는 protected로 정의한 메서드를 참조할 때 사용하며 static 메서드를 호출하는 것과 유사합니다. 주로 스트림에서 필터와 매핑용도로 많이 사용합니다. 여기서 비한정적이란 작성하는 구문 자체가 특정한 객체를 참조하기 위한 변수를 지정하지 않는다란 의미입니다. 예를 들어 보겠습니다.

String::toUpperCase

String 클래스의 toUpperCase 메서드는 public이나 static이 아니라 반드시 String 클래스가 객체화 되야 호출할 수 있습니다. 그런데 위 코드를 보면 static 메서드를 참조하는 것처럼 정의가 되어있습니다. 이 코드를 람다식으로 바꾸면

(String str) -> str.toUpperCase()

위 코드를 보시면 객체의 생성을 파라미터로 받았습니다. -> 람다 표현식 내부에서 객체 생성이 생겨 객체를 참조할 만한 변수가 외부에 존재하지 않습니다. 그래서 수신객체를 알려주는데 str::toUpperCase 라 표현할 수 없어 String::toUpperCase라 표현하고 String 이라는 수신객체를 알려줍니다.

만약 처리해야할 데이터가 위 사례처럼 하나면 상관없으나, 파라미터가 여러개면 너무 많이 생략하다보니 이 메서드 참조가 어디서 어디까지 한지 알 수가 없습니다. 그래서 주로 스트림에서 많이 사용하는 듯합니다. (매핑, 필터 작업)

한정적 메서드 참조

이미 외부에서 선언된 객체의 메서드를 호출하거나, 객체를 직접 생성해서 메서드를 참조할 때 사용합니다. 여기서 한정적이란 메서드가 특정 객체의 변수로 제한되는걸 말합니다. 예시를 들어보겠습니다.

Calendar.getInstance()::getTime

위 코드 보시면 :: 앞에 객체를 생성하는 코드가 작성 :: 뒤에는 해당 객체에서 호출할 수 있는 getTIme을 지정했습니다. 자바 8이후로 많은 클래스가 생성자를 이용해서 객체 인스턴스하는 것보단 of와 같은 메서들르 이용해서 생성하는 경향이 있습니다. 람다식으로 바꾸면 다음과 같습니다.

Calendar cal = Calendar.getInstance();
() -> cal.getTime()

위 메서드 참조는 큰 단점이 있습니다. 메서드 참조에 의해서 값을 처리할 때마다 Calendar 객체를 생성하여 불필요한 낭비가 발생하게 됩니다. 그래서 한정적 메서드 참조할 때 외부에서 한 번 생성하고 참조하는 형식을 사용합니다.

Calendar cal = Calendar.getInstance(); // 객체 생성
cal::getTime // 메서드 참조. cal 변수를 참조합니다.

람다 표현식을 메서드 참조 방식으로 변경하면 더욱 간결하고 명확하게 표현할 수 있다. 또한 매개변수 수가 늘어날수록 메서드 참조로 제거할 수 있는 코드량도 늘어난다. 단, 람다가 메서드 참조보다 간결하게 표현되는 경우에는 람다식을 이용하는 편이 좋다.

X Tutup