티스토리 뷰

Item43에서는 람다보다 메서드 참조를 사용하라 말한다

메서드 참조를 사용하는 이유는 간결함 때문이다

람다를 사용해 익명 함수를 대체하고 간결하게 표현하기로 한 거 끝까지 가보자는 것이다

메서드 참조를 사용하는 방법은 네 가지가 있다

 

https://www.baeldung.com/java-method-references

 

 

1. static method reference

<클래스::정적 메서드> 형태로 참조한다

2. instance method reference (bound receiver)

<인스턴스::인스턴스 메서드> 형태로 참조한다

3. instance method reference (unbound receiver)

<특정 타입의 아무 인스턴스::인스턴스> 메서드 형태로 참조한다

4. constructor reference

<클래스::new> 연산자 형태로 참조한다

 

이 중 한정적과 비한정적의 차이가 확 와닿지 않아서 좀 더 찾아봤다

책에서는 수신 객체를 특정하느냐 아니냐로 한정적과 비한정적 메서드 참조를 설명한다

한정적 메서드 참조를 설명할 때 Instance.now()::isAfter를 예시로 드는데 이것 땜시 헷갈렸다

 

아래의 글에서는 조금 더 직접적으로 설명하는데

메서드 참조를 하는 시점에 메서드를 참조할 외부 인스턴스가 존재하고 있느냐 아니냐로 나눌 수 있다

bound 한정적이라는 것은 외부 인스턴스가 이미 존재하고 있어야 함을 의미하고

unbound 비한정적이라는 것은 외부 인스턴스는 없어도 되고 참조하는 시점에만 인스턴스가 존재하면 된다는 것을 의미한다

 

https://stackoverflow.com/questions/35914775/java-8-difference-between-method-reference-bound-receiver-and-unbound-receiver

 

쉽게 말해 한정적 메서드 참조는 이미 존재하는 인스턴스의 메서드를 참조하는 것이다

이미 존재하는 인스턴스의 메서드에 묶여 있으니 bound, 한정적이라는 명칭을 붙인 것 같다

스트림으로 돌릴 때 돌아가는 요소의 타입은 알고 있지만 인스턴스의 값은 알지 못 하는 상태다

그러니 특정 타입인 것만 알고 어떤 인스턴스가 와도 무방하니 이를 비한정적 메서드 참조라 하는 것이다

 

아래 코드 예시로 보면 스트림으로 돌릴 때 Bicycle 타입인 것을 알고 있으니

Bicycle::getBrand, Bicycle::getFrameSize는 비한정적 메서드 참조이고 어떤 Bicycle 인스턴스에도 적용할 수 있다

bicycle3 인스턴스의 메서드를 참조할 때는 이미 존재하는 bicycle3에 묶여 있는 상태로 참조하니 한정적 메서드 참조다

 

미묘한 차이라 반복 설명을 하게 되는데 사실 이렇게까지 알 필요는 없을 듯 하다, 어차피 IDE가 친절히 알려줄 것이다

한정적, 비한정적 메서드 참조로 아직 호되게 당해본 적이 없어서 그럴 수도 있지만..?

Bicycle bicycle1 = new Bicycle("panda", 55);
Bicycle bicycle2 = new Bicycle("bear", 66);

// unbound method reference
List<String> strings = bicycles.stream()
  .map(Bicycle::getYaho)
  .toList();
  
// bound method reference
Bicycle bicycle3 = new Bicycle("yahoo", 77);

List<String> strings = bicycles.stream()
  .map(bicycle3::getYaho)
  .toList();

 

더 궁금하다면 Bicycle 예제 코드를 보면서 직접 메서드 참조를 사용해보자

public class MethodReferenceExample {

  public static void main(String[] args) {
    Bicycle bicycle1 = new Bicycle("panda", 55);
    Bicycle bicycle2 = new Bicycle("bear", 66);

    List<Bicycle> bicycles = List.of(bicycle1, bicycle2);

    BicycleComparator bicycleComparator = new BicycleComparator();
    List<Bicycle> sortedBicycles = bicycles.stream()
      .sorted(bicycleComparator::compare)
      .toList();

    System.out.println("sortedBicycles = " + sortedBicycles);
  }

  @Data
  @NoArgsConstructor
  @AllArgsConstructor
  public static class Bicycle {

    private String brand;
    private Integer frameSize;
  }

  public static class BicycleComparator implements Comparator<Bicycle> {

    @Override
    public int compare(Bicycle o1, Bicycle o2) {
      return o1.getFrameSize().compareTo(o2.getFrameSize());
    }
  }
}

 

자바 17과 친해지고 싶어 이펙티브 자바를 17로 공부 중인데 

인텔리제이가 bicycleCompartor::compare를 bicycleCompartor로 바꾸라 알려준다

자바 17에서는 compare 마저 줄일 수 있는 건가?! 싶어서 자바 8로 바꿔봤는데도 여전히 알려주는 것으로 보아

새로 들어온 기능은 아니고 Comparator가 compare만 가지고 있는 @FunctionalInterface라 그런 것 같다

 

 

끝으로 다양한 메서드 참조를 써보면서 느낀 점은 아이템43의 조언은 깡그리 무시하고 람다로 다 발라놔도 무방하다는 점이다

간결한 표현을 위해 메서드 참조를 사용하는 것인데 이에 따른 단점으로는 메서드 시그니처를 알고 있어야 한다는 것이다

메서드 참조를 사용하면 표현은 더 간결해질지 몰라도 <클래스명 또는 인스턴스명::메서드 이름>만 덩그러니 있게 된다

 

따라서 Integer::sum, String::toUpperCase 같은 건 직관적이니 쉽게 알 수 있어도

직접 만든 메서드의 경우엔 어떤 작용을 하는지 메서드 내부로 들어가봐야 알 수 있다

게다가 작성한 본인은 메서드 참조를 쉽게 파악할 수 있어도 남이 볼 땐 아닐 수 있다

간결함과 이해 용이성의 트레이드오프니 익숙해지면 메서드 참조로 하고 이것 저것 따지기 귀찮으면 람다로 다 발라놓자

댓글
링크
글 보관함
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Total
Today
Yesterday