[아이템 43] 람다보다는 메서드 참조를 사용하라
2023. 5. 13. 15:51ㆍJava/Effective Java
메서드 참조(Method Reference)
- 함수를 메서드의 파라미터로 전달하는 것을 메서드 참조
- class::methodName 구문을 사용하여 클래스 또는 객체에서 메서드를 참조
- 람다식(Lambda Expression)의 가장 큰 장점 중 하나는 코드가 짧아진다는 것인데, 람다식에 메서드 참조를 사용하면 코드를 더 간결하고 가독성 있게 만들 수 있다.
※ 자바8 Map merge 메서드
- 키, 값, 함수를 인수를 받으며, 주어진 키가 맵 안에 없다면 주어진 [키, 값] 쌍을 그대로 저장
- 주어진 키가 맵 안에 있다면 함수(세번째 인수로 받은)를 현재 값과 주어진 값에 적용한 다음, 그결과로 현재 값을 덮어쓴다. 즉, [키, 함수의 결과] 쌍을 저장한다.
Map<String, Integer> keyCount = new HashMap<>();
keyCount.merge("A", 1, Integer::sum); // 메서드 참조 사용
keyCount.merge("A", 1, Integer::sum);
keyCount.merge("A", 1, Integer::sum);
keyCount.merge("B", 1, Integer::sum);
keyCount.merge("C", 1, Integer::sum);
keyCount.merge("C", 1, (count, increment) -> count + increment); // 람다 사용
for(String key : keyCount.keySet()) {
System.out.println(key + " : " + keyCount.get(key));
}
-- 출력 결과 --
A : 3
B : 1
C : 2
메서드 참조 유형
메서드 참조 유형 | 메서드 참조 | 람다 |
정적 | 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] |
정리
- 메서드 참조는 람다의 간단명료한 대안이 될 수 있다.
- 메서드 참조 쪽이 짧고 명확하다면 메서드 참조를 쓰고, 그렇지 않을 때만 람다를 사용하라
궁금한점
1. 어떤 람다에서는 매개변수의 이름 자체가 프로그래머에게 좋은 가이드가 되기도 한다. 이런 람다는 길이는 더 길지만 메서드 참조보다 읽기 쉽고 유지보수도 쉬울수 있다.
- 아래의 예의 경우, 람다를 사용한 경우가 좀 더 읽기 쉽고 명확해 더 좋은 코드인것 같다.
Consumer<String> printString1 = (str) -> System.out.println(str); // 람다 사용
Consumer<String> printString2 = System.out::println; // 메서드 참조
2. 한정적 메서드 참조와 비한정적 메서드 참조의 차이점
1) 비한정적 메서드 참조
- 비한정적(unbound) 메서드 참조는 static 메서드를 참조하는 것과 유사
- 비한정적이라는 표현은 작성하는 구문 자체가 특정한 객체를 참조하기 위한 변수를 지정하지 않는다는 의미
- String 클래스의 toUpperCase 메서드는 public 메서드이며 static이 아니기 때문에 반드시 String 클래스가 객체화되어야만 호출 할 수 있다. 그러나, 마치 static 메서드를 참조하는 것처럼 정의
// 비한정적 메서드 참조
String::toUpperCase
- 람다로 표현한 내용을 풀어서 보면, 객체의 생성을 파라미터로 받음
- 즉, 람다 표현식 내부에서 객체 생성이 일어났기 때문에 객체를 참조할만한 변수가 외부에 존재하지 않는다.
// 람다로 표현
(String str) -> str.toUpperCase()
- 처리해야 하는 데이터가 여러개라면 조금 복잡해질 수 있음 List에서 연속된 2개의 데이터를 뽑아서 크기를 구하는 방식을 작성하면 다음과 같다. 매우 함축적이라, 이해하고 해석하는데 어려울 수 있다.
List<String> list =new ArrayList<>();
// 동일 표현
list.stream().sorted((String a, String b) -> a.compareTo(b));
list.stream().sorted(String::compareTo);
2) 한정적 메서드 참조
- 한정적(bound)이라는 단어를 사용한 이유는 참조하는 메서드가 특정 객체의 변수로 제한되기 때문
Calendar.getInstance()::getTime
Calendar cal = Calendar.getInstance(); // 객체 생성
() -> cal.getTime() // 람다
Calendar cal = Calendar.getInstance(); // 객체 생성
cal::getTime // 메서드 참조 구문. cal 변수를 참조
3) 결론
한정적 메서드 참조는 외부에서 정의한 객체의 메서드를 참조할 때 사용하며, 비한정적 메서드 참조는 람다 표현식 내부에서 생성한 객체의 메서드를 참조할 때 사용한다.
'Java > Effective Java' 카테고리의 다른 글
[아이템 47] 반환 타입으로는 스트림보다 컬렉션이 낫다 (0) | 2023.05.31 |
---|---|
[아이템 46] 스트림에서는 부작용 없는 함수를 사용하라 (0) | 2023.05.30 |
[아이템 45] 스트림은 주의해서 사용하라 (1) | 2023.05.17 |
[아이템 42] 익명 클래스보다는 람다를 사용하라 (0) | 2023.05.12 |
[아이템 5] 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2021.03.23 |