📍매개변수를 받아서, 그 매개변수로 같은 메서드를 호출해야할 때
public static void main(String[] args) {
BinaryOperator<Integer> add1 = (x, y) -> add(x, y);
BinaryOperator<Integer> add2 = (x, y) -> add(x, y);
Integer result1 = add1.apply(1, 2);
System.out.println("result1 = " + result1);
Integer result2 = add2.apply(1, 2);
System.out.println("result2 = " + result2);
}
static int add(int x, int y) {
return x + y;
}
지금처럼 함수를 전달해야하는 상황에서 로직을 보자. add1, add2는 단순하게 매개변수를 받고, 그 매개변수로 add 메서드를 호출하는 로직이다. 이럴 때 메서드 참조를 사용할 수 있다.
public static void main(String[] args) {
BinaryOperator<Integer> add1 = MethodRefStartV3::add; // (x, y) ->
add(x, y)
BinaryOperator<Integer> add2 = MethodRefStartV3::add; // (x, y) ->
add(x, y)
Integer result1 = add1.apply(1, 2);
System.out.println("result1 = " + result1);
Integer result2 = add2.apply(1, 2);
System.out.println("result2 = " + result2);
}
static int add(int x, int y) {
return x + y;
}
여기서는 메서드 참조(Method Reference) 문법인 `클래스명::메서드명` 을 사용하여 `(x, y) -> add(x, y)` 라 는 람다를 더욱 간단하게 표현했다. 이는 내부적으로 (x, y) -> add(x, y)와 동일하게 작동한다.
메서드 참조의 장점
- 메서드 참조를 사용하면 코드가 더욱 간결해지고, 가독성이 향상된다.
- 더 이상 매개변수를 명시적으로 작성할 필요가 없다. 컴파일러가 자동으로 매개변수를 매칭한다.
- 별도의 로직 분리와 함께 재사용성 역시 높아진다.
📍메서드 참조의 유형과 특징
1. 정적 메서드 참조
-
설명: 이름 그대로 정적(static) 메서드를 참조
-
문법: 클래스명::메서드명
-
예: Math::max , Integer::parseInt 등
2. 특정 객체의 인스턴스 메서드 참조
-
설명: 이름 그대로 특정 객체의 인스턴스 메서드를 참조
-
문법: 객체명::인스턴스메서드명
-
person::introduce , person::getName 등
3. 생성자 참조
-
설명: 이름 그대로 생성자를 참조한다.
-
문법: 클래스명::new
-
예: Person::new
4. 임의 객체의 인스턴스 메서드 참조
- 설명: 첫 번째 매개변수(또는 해당 람다가 받을 대상)가 그 메서드를 호출하는 객체가 된다.
-
문법: 클래스명::인스턴스 메서드명
-
예: Person::introduce , 같은 람다: (Person p) -> p.introduce()
❓메서드 참조에서 ()를 사용하지 않는 이유
메서드 참조의 문법을 잘 보면 뒤에 메서드명 뒤에 `()` 가 없다.
예를 들어서 `Person::greeting()` 이 아니라, `Person::greeting` 으로 표현한다.
`()` 는 메서드를 즉시 호출한다는 의미를 가진다. 여기서 `()` 가 없는 것은 메서드 참조를 하는 시점에는 메서드를 호출하는 게 아니라 단순히
메서드의 이름으로 해당 메서드를 참조만 한다는 뜻이다.
해당 메서드의 실제 호출 시점은 함수형 인터페이스를 통해서 이후에 이루어진다.
❓메서드 참조에서 매개변수를 생략하는 이유
함수형 인터페이스의 시그니처(매개변수와 반환 타입)가 이미 정해져 있고, 컴파일러가 그 시그니처를 바탕으로 메서드 참조와 연결해 주기 때문에 명시적으로 매개변수를 작성하지 않아도 자동으로 추론되어 호출된다.
예를 들어, Function<String, String> 이라는 함수형 인터페이스가 입력: `String`
출력: `String` 이라는 시그니처를 갖고 있기 때문에, Person::greetingWithName 를 보면 컴파일러가 "이 Function<String, String> 을 만족시키려면 greetingWithName(String name) 을 호출하면 되겠구 나"하고 자동으로 판단해 매개변수를 전달한다.
이렇게 매개변수를 포함한 메서드 호출도 메서드 참조를 사용하면 더욱 간편해진다.
📍임의 객체의 인스턴스 메서드 참조?
1, 2, 3번 예시는 직관적이다. 단순히 객체의 메서드를 호출하면 끝나는 람다식이다. 하지만 임의 객체의 인스턴스 메서드 참조가 정확히 무엇일까?
public static void main(String[] args) {
Function<Person, String> fun2 = Person::introduce; // 타입::인스턴스메서드
System.out.println("person1.introduce = " + fun2.apply(person1));
System.out.println("person2.introduce = " + fun2.apply(person2));
System.out.println("person3.introduce = " + fun2.apply(person3));
}
이 람다는 Person 타입을 매개변수로 받는다. 그리고 매개변수로 넘겨받은 person 인스턴스의 introduce() 인스턴스 메서드를 호출한다.
그리고 앞선 3가지 경우와의 가장 중요한 차이점 = 임의 객체의 인스턴스 메서드 참조는 선언 시점에 호출할 인스턴스를 지정하지 않는다. 대신에 호출 대상을 매개변수로 선언해두고, 실행 시점에 호출할 인스턴스를 받는다. 실행 시점이 되어야 어떤 객체가 호출되는지 알 수 있으므로 "임의 객체의 인스턴스 메서드 참조"라 한다.
'java' 카테고리의 다른 글
[Java] 람다 VS 익명 클래스 (0) | 2025.05.12 |
---|---|
[Java] 람다의 활용 (0) | 2025.05.05 |
[Java] 함수형 인터페이스 (0) | 2025.04.11 |
[Java] 람다가 필요한 이유와 생략 규칙 (0) | 2025.04.07 |
[Java] 튜닝의 마지막 단계 GC 알아보기 (0) | 2025.02.05 |