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

2024. 11. 12. 23:02·1️⃣ 백앤드/이펙티브 자바

1. 메서드 참조

메서드 참조

분명히 앞선 아이템 41에서, 익명클래스보다는 람다를 사용하라는 내용이 있었다. 바로 간결함 때문이다. 그런데, 함수 객체를 람다보다도 더 간결하게 만드는 방법이 있었다. 바로 메서드 참조이다. 함수 객체가 뭐였는가? 예전 자바에서 함수 타입을 표현할 땐 추상메서드를 하나만 담은 인터페이스의 인스턴스를 함수 객체라고 불렀다. 이를 매우 간단하게 만드는 방법이 메서드 참조라니, 아래에서 더 알아보자. Map 의 merge 메서드를 살펴보자.

map.merge(key, 1, (count, incr) -> count + incr);

키가 맵 안에 없다면 키와 숫자 1을 매핑하고, 이미 있다면 기존 매핑 값을 증가시키는 내용이다. 아래 예시처럼 사용된다.

import java.util.HashMap;
import java.util.Map;

public class MapMergeExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();

        // 초기값 설정
        map.put("apple", 3);
        map.put("banana", 2);

        // 키 "apple"에 값 2를 추가
        map.merge("apple", 2, (oldValue, newValue) -> oldValue + newValue);
        System.out.println("After merging apple: " + map); // {apple=5, banana=2}

        // 키 "orange"가 없으므로 새로 추가
        map.merge("orange", 4, (oldValue, newValue) -> oldValue + newValue);
        System.out.println("After merging orange: " + map); // {apple=5, banana=2, orange=4}
    }
}

물론 깔끔해 보일 수 있지만, 매개변수인 count 와 incr , 예시에서는 oldValue 와 newValue 는 크게 하는 일이 없다. 두 인수의 합을 단순히 반환할 뿐이다. 자바 8 이후 Integer 클래스 (와 모든 기본타입의 박싱 타입) 는 람다와 기능이 같은 정적 메서드 sum 을 제공한다. 이를 메서드 참조를 이용한다고 한다. 메서드 참조를 활용해 수정해보자.

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

매개변수의 이름 자체를 사용할 수 없어 뜻을 이해하기 어렵고 유지보수가 어려울 순 있다. 그래도 메서드 참조를 사용하는 편이 더 짧고 간결하다는 강점이 있다. 그래서 보통 람다로 작성할 코드가 너무 길거나 복잡하다면 이를 새로운 메서드에 담은 다음, 람다 대신 그 메서드 참조를 사용한다.

 

2. 람다가 메서드 참조보다 간결할 때

IDE 는 람다를 메서드 참조로 대체하라고 권고할 것이지만, 람다가 메서드 참조보다 간결할 때도 있다. 주로 메서드와 람다가 같은 클래스에 있을 때 그렇다. 아래 두 케이스가 GoshThisClassNameIsHumongous 클래스 안에 같이 있다고 해보자.

service.execute(GoshThisClassNameIsHumongous::action);
//메서드 참조 

service.execute(() -> acation());
//람다 사용

람다 쪽이 더 짧고, 명확하다. 

 

3. 메서드 참조의 다섯가지 유형

메서드 참조 유형 예 같은 기능을 하는 람다
정적 Integer::parseInt str -> Integer.parseInt(str)
한정적(인스턴스) Instance.now()::isAfter Instance 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]

정적 메서드 참조는 클래스의 정적 메서드를 참조하는 방식입니다.

// 정적 메서드 참조
Function<String, Integer> func = Integer::parseInt;
System.out.println(func.apply("123"));  // 출력: 123

// 람다 표현식
Function<String, Integer> func = str -> Integer.parseInt(str);
System.out.println(func.apply("123"));  // 출력: 123

한정적(인스턴스) 메서드 참조는 특정 객체의 인스턴스 메서드를 참조하는 방식입니다. 예시로 나온 then::isAfter는 이미 선언된 then 인스턴스의 isAfter 메서드를 참조합니다.

// 한정적 메서드 참조
Instant then = Instant.now();
Predicate<Instant> func = then::isAfter;
System.out.println(func.test(Instant.now().minusSeconds(3600)));  // true or false

// 람다 표현식
Instant then = Instant.now();
Predicate<Instant> func = t -> then.isAfter(t);
System.out.println(func.test(Instant.now().minusSeconds(3600)));  // true or false

 

비한정적(인스턴스) 메서드 참조는 특정 인스턴스를 지정하지 않고, 클래스 타입의 인스턴스 메서드를 참조하는 방식입니다.

// 비한정적 메서드 참조
Function<String, String> func = String::toLowerCase;
System.out.println(func.apply("HELLO"));  // 출력: hello

// 람다표현식
Function<String, String> func = str -> str.toLowerCase();
System.out.println(func.apply("HELLO"));  // 출력: hello

클래스 생성자 참조는 특정 클래스의 생성자를 참조하여 객체를 생성하는 방식입니다.

// 클래스 생성자 참조
Supplier<TreeMap<Integer, String>> func = TreeMap::new;
TreeMap<Integer, String> map = func.get();
map.put(1, "One");
System.out.println(map);  // 출력: {1=One}

// 람다 표현식
Supplier<TreeMap<Integer, String>> func = () -> new TreeMap<Integer, String>();
TreeMap<Integer, String> map = func.get();
map.put(1, "One");
System.out.println(map);  // 출력: {1=One}

 

배열 생성자 참조는 배열의 생성자를 참조하여 배열을 생성하는 방식입니다.

// 배열 생성자 참조
IntFunction<int[]> func = int[]::new;
int[] arr = func.apply(5);  // 길이가 5인 배열 생성
System.out.println(arr.length);  // 출력: 5

// 람다 표현식
IntFunction<int[]> func = len -> new int[len];
int[] arr = func.apply(5);  // 길이가 5인 배열 생성
System.out.println(arr.length);  // 출력: 5

 

 

'1️⃣ 백앤드 > 이펙티브 자바' 카테고리의 다른 글

[아이템45] 스트림은 주의해서 사용하라  (0) 2024.11.14
[아이템44] 표준 함수형 인터페이스를 사용하라  (1) 2024.11.13
[아이템42] 익명 클래스보다는 람다를 사용하라  (2) 2024.11.11
[아이템41] 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라  (2) 2024.11.07
[아이템40] @Override 애너테이션을 일관되게 사용하라  (1) 2024.11.07
'1️⃣ 백앤드/이펙티브 자바' 카테고리의 다른 글
  • [아이템45] 스트림은 주의해서 사용하라
  • [아이템44] 표준 함수형 인터페이스를 사용하라
  • [아이템42] 익명 클래스보다는 람다를 사용하라
  • [아이템41] 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라
HOZINU
HOZINU
주니어 백앤드 개발자의 세상만사 이모저모. 주로 개발 이야기를 다룸.
  • HOZINU
    백엔드 탐험 일지
    HOZINU
  • 전체
    오늘
    어제
  • 블로그 메뉴

    • ⛪ HOME
    • 🌍 GITHUB
    • 카테고리 (73)
      • 1️⃣ 백앤드 (72)
        • 이펙티브 자바 (72)
      • 2️⃣ CS (0)
        • 운영체제 (0)
        • 네트워크 기초 (0)
        • 네트워크 응용 (0)
        • SSL & PKI (0)
        • 기타 (0)
      • 3️⃣ 코딩테스트 (0)
      • 4️⃣ 개인공부 (0)
        • MSA (0)
        • REDIS (0)
      • 5️⃣ 일상이야기 (1)
  • 인기 글

  • 태그

    hashcode
    계층구조
    로타입
    Comparable
    캡슐화
    Cleaner
    맥북
    equals
    빌더
    try-with-resources
    의존객체
    멤버클래스
    CLONE
    optional
    표준예외
    finalizer
    싱글턴
    컴포지션
    정적 팩터리 메서드
    정보은닉
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
HOZINU
[아이템43] 람다보다는 메서드 참조를 사용하라
상단으로

티스토리툴바