Java 8 메서드 참조(::) 동작 원리와 활용

메서드 참조란

Java 8에서 도입된 :: 연산자는 메서드 참조(Method Reference)를 나타냅니다. 이는 람다 표현식을 더욱 간결하게 작성할 수 있는 문법적 편의 기능입니다. 함수형 인터페이스의 추상 메서드 구현체로 기존에 정의된 메서드를 직접 참조할 수 있게 해줍니다.

함수형 인터페이스와 람다의 관계

메서드 참조를 이해하려면 먼저 함수형 인터페이스의 개념이 필요합니다. 단 하나의 추상 메서드만 보유한 인터페이스를 함수형 인터페이스라 하며, @FunctionalInterface 어노테이션으로 명시할 수 있습니다.

@FunctionalInterface
interface StringProcessor {
    String execute(String input);
}

이러한 인터페이스는 람다 표현식으로 즉시 구현 가능합니다:

StringProcessor upperCase = (text) -> text.toUpperCase();

메서드 참조의 4가지 유형

1. 정적 메서드 참조

List<Integer> numbers = Arrays.asList(1, -2, 3, -4);
numbers.stream()
       .map(Math::abs)  // (n) -> Math.abs(n)
       .forEach(System.out::println);

2. 특정 객체의 인스턴스 메서드 참조

StringBuilder buffer = new StringBuilder();
Stream.of("Java", "Python", "Go")
      .forEach(buffer::append);  // (s) -> buffer.append(s)

3. 임의 객체의 인스턴스 메서드 참조

List<String> words = Arrays.asList("apple", "BANANA", "Cherry");
words.sort(String::compareToIgnoreCase);  // (a, b) -> a.compareToIgnoreCase(b)

4. 생성자 참조

Supplier<ArrayList<String>> factory = ArrayList::new;  // () -> new ArrayList<>()
List<String> created = factory.get();

실제 동작 분석

Iterable.forEach의 시그니처를 살펴보면 메서드 참조가 어떻게 작동하는지 명확히 이해할 수 있습니다:

default void forEach(Consumer<? super T> operation) {
    Objects.requireNonNull(operation);
    for (T element : this) {
        operation.accept(element);
    }
}

Consumer<T>void accept(T t) 단일 메서드를 가진 함수형 인터페이스입니다. System.out.println(Object)는 이 시그니처와 완벽히 일치하므로, 다음 두 표현은 동일합니다:

표현 방식설명
x -> System.out.println(x)명시적 람다
System.out::println바인딩된 메서드 참조

복합 활용 예제

public class MethodReferenceDemo {
    public static void main(String[] args) {
        // 메서드 참조를 파이프라인에 조합
        Stream.of("  hello  ", "WORLD", "  Java  ")
              .map(String::trim)           // 임의 객체 메서드 참조
              .map(String::toLowerCase)
              .filter(s -> s.length() > 3)
              .map(MethodReferenceDemo::wrapWithBrackets)  // 정적 메서드 참조
              .forEach(System.out::println);               // 바인딩된 인스턴스 메서드 참조
    }
    
    static String wrapWithBrackets(String content) {
        return "[" + content + "]";
    }
}

주의사항

메서드 참조는 다음 경우에 적합하지 않을 수 있습니다:

  • 매개변수를 변형하거나 추가 연산이 필요한 경우: list.forEach(x -> System.out.println("값: " + x))
  • 메서드 시그니처가 함수형 인터페이스와 불일치하는 경우
  • 가독성을 해치는 과도한 체이닝

이러한 상황에서는 명시적인 람다 표현식이나 별도 메서드 분리가 더 적절한 선택입니다.

태그: Java 8 Lambda Expression Method Reference Functional Interface Stream API

7월 21일 05:05에 게시됨