메서드 참조란
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)) - 메서드 시그니처가 함수형 인터페이스와 불일치하는 경우
- 가독성을 해치는 과도한 체이닝
이러한 상황에서는 명시적인 람다 표현식이나 별도 메서드 분리가 더 적절한 선택입니다.