☕ Java/이론

[Java] 메소드 참조 (Method References)

an2z 2022. 2. 4. 18:10

메소드 참조

메소드 참조는 람다식으로 줄어든 코드의 양을 조금 더 줄일 수 있게 한다.

따라서 메소드 참조가 유용한 상황에서는 람다식이 아닌 메소드 참조를 사용해 해결 할 수 있다.

 

메소드 참조의 유형은 다음과 같이 4가지로 정리할 수 있다.

  • static 메소드 참조
  • 참조변수를 통한 인스턴스 메소드 참조
  • 클래스 이름을 통한 인스턴스 메소드 참조
  • 생성자 참조

 

static 메소드 참조

  • 자바 8부터 이미 정의되어 있는 메소드를 사용할 때 람다식을 작성할 필요 없이 메소드 정보만 전달할 수 있다.
  • 대입연산자 오른편에 메소드에 대한 정보만 전달되면 전달된 인자를 그대로 넘겨주면서 해당 메소드를 호출하라는 의미이다.
클래스 이름 :: static 메소드 이름
Consumer<List<Integer>> c = l -> Collections.reverse(l);

Consumer<List<Integer>> c = Collections::reverse;
// accept 메소드 호출시 전달되는 인자를 
// reverse 메소드를 호출하면서 그대로 전달한다는 약속에 근거하여 수정
더보기
class MethodReferences_static {
    public static void main(String[] args) {
        List<Integer> li = Arrays.asList(1, 3, 5, 7, 9);
        li = new ArrayList<>(li);

        // Consumer<List<Integer>> c = l -> Collections.reverse(l); // 람다식
        Consumer<List<Integer>> c = Collections::reverse; // 메소드 참조

        c.accept(li); // 순서 뒤집기, 전달 인자 li를 reverse에 그대로 전달하게 됨
        System.out.println(li); // 출력
    }
}
// Consumer<T> void accept(T t)
[9, 7, 5, 3, 1]

 

참조변수를 통한 인스턴스 메소드 참조

  • 참조변수가 final로 선언되었거나 effectively final인 경우에만 람다식과 메소드 참조를 허용한다.
    • 한번 참조한 대상을 바꾸지 않는 참조변수를 effectively final(사실상 상수)이라 한다.
    • 람다식이나 메소드 참조로 생성된 인스턴스 내에서 final로 선언되지 않았거나 effectively final이 아닌 참조변수를 참조하게 하는 것은 논리적 혼란 또는 예측 불가능한 상황으로 이어질 수 있기 때문에 제한을 두는 것이다.
참조변수 이름 :: 인스턴스 메소드 이름
Consumer<List<Integer>> c = e -> js.sort(e); 

Consumer<List<Integer>> c = js::sort;
더보기
class MethodReferences_EffectivelyFinal {
    public static void main(String[] args) {
        List<Integer> ls = Arrays.asList(1,3,5,7,9);
        ls = new ArrayList<>(ls);

        JustSort js = new JustSort();

        // Consumer<List<Integer>> c = e -> js.sort(e); // 람다식
        Consumer<List<Integer>> c = js::sort; // 메소드 참조

        c.accept(ls);
        System.out.println(ls);
    }
}
// Consumer<T> void accept(T t)
[9, 7, 5, 3, 1]

 

🔍 forEach 메소드

  • Iterable<T> 인터페이스에 정의 되어 있는 디폴트 메소드이다.
  • Collection<E> 인터페이스는 Iterable<T> 인터페이스를 상속하기 때문에 대부분의 컬렉션 클래스들은 Iterable<T>를 구현하므로 forEach 메소드 호출이 가능하다.
  • forEach 메소드 호출을 위해 Consumer<T> 인터페이스에 대한 람다식 또는 메소드 참조를 전달해야 한다.
default void forEach(Consumer<? super T> action) {
    for (T t : this) // this는 이 메소드가 속한 컬렉션 인스턴스를 의미
        action.accept(t) // 모든 저장된 데이터들에 대해 이 문장 반복
}
class MethodReferences_ForEach {
    public static void main(String[] args) {
        List<String> ls = Arrays.asList("Red", "Black");

        // ls.forEach(s -> System.out.println(s)); // 람다식 기반
        ls.forEach(System.out::println); // 메소드 참조 기반
    }
}
Red
Black
Red
Black

 

인스턴스 없이 인스턴스 메소드 참조

  • 첫번째 전달인자를 대상으로 인스턴스의 메소드를 호출하면 두번째 이후의 전달인자를 그대로 전달하기로 약속되어 있다.
클래스 이름 :: 인스턴스 메소드
class IBox {
    private int n;
    public IBox(int n) {
        this.n = n;
    }

    public int larger(IBox b) { // 인스턴스 메소드
        if (n > b.n)
            return n;
        else
            return b.n;
    }
}

class MethodReferences_NoObject {
    public static void main(String[] args) {
        IBox ib1 = new IBox(5);
        IBox ib2 = new IBox(7);

        // 두 상자에 저장된 값 비교하여 더 큰 값 반환
        // ToIntBiFunction<IBox, IBox> bf = (b1, b2) -> b1.larger(b2); // 람다식 기반
        ToIntBiFunction<IBox, IBox> bf = IBox::larger; // 메소드 참조 기반

        int bigNum = bf.applyAsInt(ib1, ib2);
        System.out.println(bigNum);
    }
}
// ToIntBiFunction<T, U> int applyAsInt(T t, U u)
7

위 예제에서 applyAsInt 메소드가 호출되면 다음과 같이 실행된다.

  • ib1.larger(ib2);

 

생성자 참조

  • 람다식 작성 시 인스턴스 생성 후 이의 참조 값을 반환하는 경우 생성자 참조를 사용할 수 있다.
클래스이름 :: new
Function<char[], String> f = ar -> { return new String(ar); }; 

Function<char[], String> f = ar -> new String(ar);

Function<char[], String> f = String::new;
더보기
class MethodReferences_StringMaker {
    public static void main(String[] args) {
        // Function<char[], String> f = ar -> new String(ar); // 람다식
        Function<char[], String> f = String::new; // 생성자 참조	

        char[] src = {'R', 'o', 'b', 'o', 't'};
        String str = f.apply(src);
        System.out.println(str);
    }
}
// Function<T, R> R apply(T t)
Robot