💻 Java 메서드 참조
Java에서 메서드 참조(Method Reference)는 람다 표현식을 간결하게 표현할 수 있는 방법입니다. 메서드 참조는 코드의 가독성을 높이고, 불필요한 중복을 줄이는 데 유용하죠. 이번 포스트에서는 메서드 참조의 유형, 메서드 참조의 동작 방식, 그리고 특정 객체의 메서드 참조 사용에 대해 상세하게 설명드리겠습니다.
🔹 메서드 참조의 유형
메서드 참조는 네 가지 주요 유형으로 나눌 수 있습니다:
- 정적 메서드 참조 (Static Method Reference)
- 인스턴스 메서드 참조 (Instance Method Reference)
- 특정 객체의 인스턴스 메서드 참조 (Reference to an instance method of a particular object)
- 생성자 참조 (Constructor Reference)
이제 각 유형이 어떻게 동작하는지 자세히 알아보겠습니다.
Integer::parseInt와 String::toUpperCase
먼저, 정적 메서드 참조와 인스턴스 메서드 참조를 살펴보겠습니다.
- Integer::parseInt: 이 메서드 참조는- Integer클래스의 정적 메서드인- parseInt를 참조합니다.- parseInt는 문자열을 정수로 변환하는 작업을 수행합니다.
  Function<String, Integer> parseFunction = Integer::parseInt;
  Integer result = parseFunction.apply("123");
  System.out.println(result); // 123여기서 Integer::parseInt는 Function<String, Integer> 인터페이스와 매핑됩니다. 이는 입력된 String을 Integer로 변환하는 기능을 합니다. apply 메서드를 호출하면 parseInt 메서드가 실행되어 문자열 "123"이 정수 123으로 변환됩니다.
- String::toUpperCase: 이 메서드 참조는- String클래스의 인스턴스 메서드인- toUpperCase를 참조합니다. 이 메서드는 문자열을 대문자로 변환합니다.
  Function<String, String> upperCaseFunction = String::toUpperCase;
  String result = upperCaseFunction.apply("hello");
  System.out.println(result); // HELLO여기서 String::toUpperCase는 Function<String, String>과 매핑되어, 입력된 문자열을 대문자로 변환합니다. 이 메서드 참조는 어떤 특정 인스턴스에 묶여 있지 않으며, 입력되는 문자열의 toUpperCase 메서드를 호출하게 됩니다.
🔹 정적 메서드 참조, 인스턴스 메서드 참조, 생성자 참조의 차이점
이제 메서드 참조의 유형별 차이점을 살펴보겠습니다.
- 정적 메서드 참조 (ClassName::methodName): 클래스의 정적 메서드를 참조합니다. 이 참조는 클래스 자체에 대해 정적으로 정의된 메서드를 대상으로 합니다.
Function<String, Integer> parseInt = Integer::parseInt;여기서 Integer::parseInt는 Integer 클래스에 정적으로 정의된 parseInt 메서드를 참조합니다. 이 메서드는 String을 받아서 Integer로 변환합니다.
- 인스턴스 메서드 참조 (ClassName::methodName): 특정 인스턴스에 의존하지 않고, 전달된 인스턴스의 메서드를 참조합니다. 이 참조는 인스턴스 메서드이지만, 클래스 이름으로 참조되며, 실제로는 메서드가 호출될 때 전달된 객체의 메서드를 실행합니다.
  Function<String, String> upperCase = String::toUpperCase;String::toUpperCase는 전달된 String 객체의 toUpperCase 메서드를 호출합니다. 여기서 upperCase 함수는 입력된 문자열을 대문자로 변환하는 역할을 합니다.
- 특정 객체의 인스턴스 메서드 참조 (instance::methodName): 특정 객체의 메서드를 참조합니다. 이 유형의 메서드 참조는 미리 정의된 객체의 메서드를 대상으로 합니다.
  String prefix = "Hello, ";
  Function<String, String> greeting = prefix::concat;
  String result = greeting.apply("World");
  System.out.println(result); // Hello, World여기서 prefix::concat은 prefix라는 특정 String 객체의 concat 메서드를 참조합니다. 이 메서드 참조는 greeting.apply("World")가 호출될 때 prefix.concat("World")가 실행되는 방식으로 동작합니다.
- 생성자 참조 (ClassName::new): 클래스의 생성자를 참조하여 새로운 객체를 만듭니다. 이 참조는 기본 생성자 또는 매개변수가 있는 생성자와 연결될 수 있습니다.
  Supplier<List<String>> listSupplier = ArrayList::new;
  List<String> list = listSupplier.get();ArrayList::new는 ArrayList의 기본 생성자를 참조하며, get() 메서드를 호출하면 새로운 ArrayList 인스턴스가 생성됩니다. Supplier 인터페이스와 함께 사용되어, new ArrayList<>()와 동일한 효과를 제공합니다.
🔹 메서드 참조의 동작 방식
메서드 참조는 람다 표현식의 간결한 대체물로 작동하며, Java 컴파일러는 이를 분석하여 적절한 메서드 또는 생성자를 호출합니다. 컴파일러가 어떻게 메서드 참조를 해석하고 작동하는지 이해해 봅시다.
- 정적 메서드 참조: 컴파일러는 클래스명과 메서드명이 명시된 경우 이를 정적 메서드로 인식합니다. 예를 들어 Integer::parseInt는Integer클래스의 정적 메서드인parseInt로 매핑됩니다.이 경우parseInt는 특정 인스턴스 없이 호출될 수 있습니다. 컴파일러는Integer::parseInt가Function<String, Integer>과 같은 인터페이스에 맞는 메서드라는 점을 인식하고 이를 사용합니다.
- Function<String, Integer> parseInt = Integer::parseInt;
- 인스턴스 메서드 참조: 클래스명만 지정된 경우, 메서드 참조는 전달된 객체의 인스턴스 메서드를 참조하는 것으로 해석됩니다. 이 경우, 컴파일러는 메서드 참조가 특정 객체의 인스턴스 메서드로 적용될 수 있는지를 판단합니다.컴파일러는 String::toUpperCase가String객체에서 호출 가능한 인스턴스 메서드라는 점을 인식합니다.
- Function<String, String> upperCase = String::toUpperCase;
- 특정 객체의 인스턴스 메서드 참조: 특정 객체가 주어지고 그 객체의 메서드가 참조되는 경우, 해당 객체의 메서드가 참조됩니다. 이는 메서드 참조가 어떤 객체에 속한 메서드인지를 컴파일러가 직접 판단하게 됩니다.컴파일러는 greeting::concat이 특정greeting객체의concat메서드를 참조한다는 것을 인식합니다. 이 참조는concatFunction.apply("World")가 호출될 때greeting.concat("World")를 수행하게 됩니다.
- String greeting = "Hello, "; Function<String, String> concatFunction = greeting::concat;
- 생성자 참조: 클래스명과 new키워드가 지정된 경우, 이를 생성자 참조로 인식합니다. 컴파일러는 생성자가 호출될 때 어떤 매개변수가 필요한지를 확인하고, 적절한 생성자를 선택합니다.여기서ArrayList::new는ArrayList의 기본 생성자를 참조하며, 컴파일러는Supplier<List<String>>인터페이스에 맞게 이 참조를 사용합니다.
- Supplier<List<String>> listSupplier = ArrayList::new;
🔹 특정 객체의 메서드 참조
특정 객체의 인스턴스 메서드를 Stream에서 메서드 참조로 사용할 수 있습니다. 이는 특정 객체를 대상으로 메서드를 호출해야 하는 경우 유용하게 사용됩니다. 이 경우 메서드 참조는 주로 람다 표현식의 대안으로 사용되며, 간결하고 가독성 높은 코드를 작성할 수 있게 해줍니다.
예시:
List<String> messages = Arrays.asList("Hello", "World");
String prefix = "Message: ";
messages.stream()
        .map(prefix::concat)  // prefix 객체의 concat 메서드 참조
        .forEach(System.out::println);
// 출력: Message: Hello, Message: World이 예제에서는 prefix::concat을 사용하여 Stream의 각 요소에 대해 concat 메서드를 호출합니다. prefix 객체에 대해 concat 메서드가 호출되어, 각 메시지에 "Message: "라는 접두어가 추가됩니다. 이처럼 특정 객체의 메서드 참조는 Stream API와 결합하여 매우 강력한 기능을 발휘할 수 있습니다.
💡 정리
- 메서드 참조 유형: 정적 메서드 참조, 인스턴스 메서드 참조, 특정 객체의 인스턴스 메서드 참조, 생성자 참조의 네 가지 주요 유형이 있습니다.
- 메서드 참조 동작 방식: Java 컴파일러는 메서드 참조를 분석하여, 적절한 메서드나 생성자를 자동으로 선택하고 실행합니다. 이 과정에서 메서드 참조가 어떤 유형에 속하는지 판단하며, 적절한 함수형 인터페이스에 맞춰 실행합니다.
- 특정 객체의 메서드 참조: Stream에서 특정 객체의 메서드 참조를 사용하면, 각 요소에 대해 해당 객체의 메서드를 간편하게 호출할 수 있습니다. 이는 코드의 가독성을 높이고, 중복을 줄이는 데 도움이 됩니다.
'Java' 카테고리의 다른 글
| [JAVA] 객체 비교와 Integer 클래스 (0) | 2024.09.09 | 
|---|---|
| [JAVA] #Deque VS Queue #ArrayDeque VS LinkedList #Map (1) | 2024.09.06 | 
| [JAVA] Java Stream API 활용법 (0) | 2024.09.02 | 
| [JAVA] 자바 버전별 차이점 (0) | 2024.08.02 | 
| [JAVA] JAVA로 REST API 구현하기 (0) | 2024.07.19 |