[Java] 메서드 참조 Method References

Published: by Creative Commons Licence

참고 문서

테스트 환경 정보

  • Java 8 이상에서 사용 가능

Java 8부터는 익명 클래스를 정의할 때 람다 식을 이용하여 코드를 단축할 수 있는데, 특정 조건에 한해 메서드 이름만으로도 같은 기능을 하는 아주 획기적인 표현식도 같이 생겼다.

… 그런데, 가끔 람다 식은 기존 메서드(existing method)를 호출하는 것 외에는 아무 것도 하지 않는 경우도 있습니다. 이러한 경우 이름으로 기존 메서드를 참조하는 것이 더 명확해집니다. 메서드 참조를 사용하면 이 작업을 수행할 수 있습니다. 이미 이름이 있는 메서드에 대해 작고 읽기 쉬운 람다 식입니다.

콜백 구조의 예시를 들어보자. 가령 아래처럼 Bar 타입의 인스턴스를 받아 say()를 실행하는 Foo가 있다고 했을 때:

interface Bar {
    void say(String str);
}

static class Foo {
    static void call(String txt, Bar callback) {
        callback.say(txt);
    }
}

Java 8 전에는 내부 클래스나 익명 클래스로 구현체(=인스턴스)를 만들었다면:

final String txt = "Hello";

// #1 내부 클래스를 이용하는 방식
Bar callback = new Bar() {
    @Override
    public void say(String str) {
        logger.debug(str);
    }
};
Foo.call(txt, callback);

// #2 익명 클래스를 이용하는 방식
Foo.call(txt, new Bar() {
    @Override
    public void say(String str) {
        logger.debug(str);
    }
});

Java 8 이후에는 아래처럼 람다 구문(statement lambda)으로 대체할 수 있는데:

// #3 1, 2를 줄여서 쓸 수 있게 나온게 람다
// Foo.call(txt, s -> logger.debug(s)); // 아래와 같음
Foo.call(txt, (s) -> {
    logger.debug(s);
});

이걸 람다 표현식(expression lambda)으로 바꾸면 이렇게 되고:

Foo.call(txt, s -> logger.debug(s));

여기서 한 술 더 뜬게 바로 메서드 참조다:

// #4 3에서 더 줄이면 메서드 참조
Foo.call(txt, logger::debug);
Foo.call(txt, System.out::println);

단, 아무 람다를 메서드 참조로 바꿀 수 있는건 아니고, 구현체의 실행부에서 파라미터를 순서와 개수 그대로(말처럼 '그대로'다. 캐스팅이나 연산 따위도 안됨) 사용해야 하며, 한 줄의 표현식만 대체할 수 있다.

메서드 참조를 사용할 수 없는 경우의 예시:

InnerClass.call((str) -> {
    logger.debug(str);
    logger.info(str);
});
// 위의 경우 메서드 참조로 작성할 수 없음.
// 메서드 참조는, 람다 표현식의 바디에서 단 하나의 호출 표현식만 작성할 경우에만 적용할 수 있다.
InnerClass.call(logger::info;logger::debug); // Syntax error on token ";", , expected