핵심 내용
아이템 42는 "익명 클래스보다는 람다를 사용하라"였다. 익명 클래스보다 람다가 더 간결하므로 특별한 경우가 아니라면 람다를 사용하라는 내용이였는데, 이번 아이템은 "람다보다는 메서드 참조를 사용하라"이다. 이번 아이템의 내용 또한 람다보다 메서드 참조가 더 간결하다면 메서드 참조를 사용하자는 내용이다.
참조 메소드
보통 람다보다 참조 메소드를 사용할 경우 더 간결해지는 편이다. 예시 1을 보자.
예시 1 - 참조 메소드를 사용하는 경우가 더 간결하다.
// 임의의 키와 Integer 값의 매핑을 관리하는 프로그램 - 람다 사용
import java.util.*;
public class Main {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
String key = "someKey";
map.merge(key, 1, (count, incr) -> count + incr); // 람다 사용
System.out.println(map);
}
}
// 임의의 키와 Integer 값의 매핑을 관리하는 프로그램 - 메소드 참조 사용
import java.util.*;
public class Main {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
String key = "someKey";
map.merge(key, 1, Integer::sum); // 메소드 참조 사용
System.out.println(map);
}
}
예시 1은 클래스명::메소드명으로 작성하여 코드가 더 명확해졌다.
또한, 매개변수를 사용하지 않아, 매개변수가 많은 메소드라면 더욱 간결해진다.
그러나 매개변수 이름이 가독성을 올려주는 경우도 있으니 고려해야 한다.
예시 2 - 람다를 사용하는 경우가 더 적합한 경우
service.execute(GoshThisClassNameIsHumongous::action); // 참조 메소드 사용
service.execute(() -> action()); // 람다 사용
예시 2의 경우, 람다가 메소드 참조보다 복잡하므로, 람다가 더 적합하다.
예시 3 - 람다를 사용하는 경우가 더 적합한 경우
java.util.funcion 패키지가 제공하는 제네릭 정적 팩토리 메소드인 Function.identity()를 사용하는 것보다 (x -> x)를 사용하는 것이 더 간결하며 명확하다.
메소드 참조의 다섯 가지 유형
| 메소드 참조 유형 | 메소드 참조 예 | 람다 예 | 설명 |
| 정적 | Integer::parseInt | str->Integer.parseInt(str) | 정적 메소드를 가리키는 메소드 참조 |
| 한정적(인스턴스) | Instant.now()::isAfter | Instant then = Instant.now() t->then.isAfter(t) |
- 참조 대상 인스턴스를 특정하는 한정적 인스턴스 메소드 참조 - 정적 참조와 비슷(함수 객체가 받는 인수와 참조되는 메소드가 받는 인수가 같다는 점에서) |
| 비한정적(인스턴스) | String::toLowerCase | str->str.toLowerCase() | - 참조 대상 인스턴스를 특정하지 않는 비한정적 인스턴스 메소드 참조 - 한정적과 달리 함수 객체를 적용하는 시점에 수신 객체를 알려줌. 따라서 수신 객체 전달용 매개변수가 매개변수 목록의 첫 번째로 들어감) - 스트림 파이프라인에서의 매핑과 필터 함수에 사용(아이템 45) |
| 클래스 생성자 | TreeMap<K, V>::new | ()->new TreeMap<K, V>() | 클래스 생성자를 가리키는 메소드 참조 |
| 배열 생성자 | int[]::new | len->new int[len] | 배열 생성자를 가리키는 메소드 참조 |
제네릭 함수 타입 구현
: 람다로 구현할 수 없다면 메소드 참조로도 구현할 수 없다.
단, 애매한 예외가 한가지 존재한다.
제네릭 함수 타입 구현은 람다로 구현할 수 없으나 메소드 참조로 구현가능한 유일한 예이다.
interface G1 {
<E extends Exception> Object m() throws E;
}
interface G2 {
<F extends Exception> String m() throws Exception;
}
interface G extends G1, G2 {
}
람다 표현식에서는 제네릭 타입을 명시적으로 선언할 수 없다. 람다 표현식의 구문에 제네릭 타입을 선언하는 방법이 없기 때문이다. 따라서 제네릭 함수 타입을 선언하는 데 람다 표현식을 사용할 수 없다.
그러나 메소드 참조를 사용하면, 해당 메소드가 이미 선언도니 타입에 따라 제네릭 타입이 자동으로 적용된다. 즉, 제네릭 함수 타입을 메소드 참조를 사용하여 구현할 수 있다.
'Language > Java' 카테고리의 다른 글
| [effective java] 아이템 45. 스트림은 주의해서 사용하라. (0) | 2023.07.03 |
|---|---|
| [effective java] 아이템 44. 표준 함수형 인터페이스를 사용하라. (0) | 2023.07.03 |
| [effective java] 아이템 42. 익명 클래스보다는 람다를 사용하라. (0) | 2023.07.02 |
| [effective java] 아이템 41. 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라. (0) | 2023.07.02 |
| [effective java] 아이템 40. @Override 애너테이션을 일관되게 사용하라. (0) | 2023.07.02 |