메서드 참조를 쉽게 말해서, "이미 정의된 메서드를 그대로 참조하여 람다 표현식을 더 간결하게 작성하는 방법" 이라고 할 수 있다. 예를 들어 (x, y) -> add(x, y) 라는 람다는 사실상 매개변수 x, y 를 그대로 add 메서드에 전달하기만 하는 코드이므로, 클래스명::메서드명 형태의 메서드 참조로 간단히 표현할 수 있다. 이렇게 하면 불필요한 매개변수 선언 없이 코드가 깔끔해지고, 가독성도 높아진다.
정리
메서드 참조는 이미 정의된 메소드를 람다로 변환하여 더욱 간결하게 사용할 수 있도록 해주는 문법적 편의 기능이다.
메서드 참조를 사용하면 이미 정의된 메서드를 장황한 람다 대신 간단하고 직관적으로 사용할 수 있다.
이처럼 람다를 작성할 때, 이미 정의된 메서드를 그대로 호출하는 경우라면 메서드 참조를 사용해 더욱 직관적이고 간결한
코드를 작성할 수 있다.
메서드 참조1 - 시작
메서드 참조는 이미 정의된 메소드를 람다로 변환하여 더욱 간결하게 사용할 수 있도록 해주는 문법적 편의 기능이다.
즉, 람다 내부에서 단순히 어떤 메서드(정적/인스턴스/생성자 등) 를 호출만하는 경우, 다음과 같은 형태로 메서드 참조를 사용할 수 있다.
(Reference to an instance method of an arbitrary object of a particular type)
이런 메서드 참조를 특정 타입의 임의 객체의 인스턴스 참조라 한다.
(실제로 메서드 참조 기능 중 가장 많이 사용된다)
여기서 줄여서 임의 객체의 인스턴스 참조라 하겠다.
임의 객체의 인스턴스 참조는 클래스명::인스턴스메서드 의 형태로 사용한다.
주의! 왼쪽이 클래스명이고, 오른쪽이 인스턴스 메서드이다!
Person::introduce 와 같이 선언하면 다음과 같은 람다가 된다.
Person::introduce
1. 왼쪽에 지정한 클래스를 람다의 첫 번째 매개변수로 사용한다.
(Person person)
2. 오른쪽에 지정한 '인스턴스 메서드'를 첫 번째 매개변수를 통해 호출한다.
(Person person) -> person.introduce()
메서드 참조4 - 활용1
임의 객체의 인스턴스 참조가 실제 어떻게 사용되는지 알아보자.
package mtehodref;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class MethodRefEx4 {
public static void main(String[] args) {
List<Person> personList = List.of(
new Person("Kim"),
new Person("Park"),
new Person("Lee")
);
List<String> result1 = mapPersonToString(personList, (Person p) -> p.introduce());
List<String> result2 = mapPersonToString(personList, Person::introduce);
System.out.println("result1 = " + result1);
System.out.println("result2 = " + result2);
List<String> upperResult1 = mapStringToString(result1, (String s) -> s.toUpperCase());
List<String> upperResult2 = mapStringToString(result1, String::toUpperCase);
System.out.println("upperResult1 = " + upperResult1);
System.out.println("upperResult2 = " + upperResult2);
}
static List<String> mapPersonToString(List<Person> personList, Function<Person, String> fun) {
List<String> result = new ArrayList<>();
for (Person p : personList) {
String applied = fun.apply(p);
result.add(applied);
}
return result;
}
static List<String> mapStringToString(List<String> strings, Function<String, String> fun) {
List<String> result = new ArrayList<>();
for (String s : strings) {
String applied = fun.apply(s);
result.add(applied);
}
return result;
}
}
람다 대신 메서드 참조를 사용한 덕분에 코드가 간결해지고, 의도가 더 명확하게 드러나는 것을 확인할 수 있다.
mapPersonToString(personList, Person::introduce)
Person 리스트에 있는 각각의 Person 인스턴스에 introduce 를 호출하고 그 결과를 리스트로 반환
mapStringToString(result2, String::toUpperCase)
String 리스트에 있는 각각의 String 인스턴스에 toUpperCase 를 호출하고 그 결과를 리스트로 반환
우리가 앞서 만든 스트림을 사용하면 리스트에 들어있는 다양한 데이터를 더 쉽게 변환할 수 있을 것 같다.
메서드 참조5 - 활용2
이번에는 스트림에 메서드 참조를 활용해보자.
예제5
package mtehodref;
import lambda.lambda5.mystream.MyStreamV3;
import java.util.List;
public class MethodRefEx5 {
public static void main(String[] args) {
List<Person> personList = List.of(
new Person("Kim"),
new Person("Park"),
new Person("Lee")
);
List<String> result1 = MyStreamV3.of(personList)
.map(person -> person.introduce())
.map(str -> str.toUpperCase())
.toList();
System.out.println("result1 = " + result1);
List<String> result2 = MyStreamV3.of(personList)
.map(Person::introduce)
.map(String::toUpperCase)
.toList();
System.out.println("result2 = " + result2);
}
}
메서드 참조의 장점
메서드 참조를 사용하면 람다 표현식을 더욱 직관적으로 표현할 수 있으며, 각 처리 단계에서 호출되는 메서드가 무엇인지 파악할 수 있다. 이처럼 람다로도 충분히 표현할 수 있지만, 내부적으로 호출만 하는 간단한 람다라면 메서드 참조가 더 짧고 명확하게 표현할 수 있다. 이런 방식은 코드 가독성을 옾이는 장점이 있다. 물론 메서드 참조 방식에 익숙해지는데는 시간이 걸린다..
메서드 참조6 - 매개변수2
이번에는 임의 객체의 인스턴스 메서드 참조에서 매개변수가 늘어나면 어떻게 되는지 알아보자.
예제6
package mtehodref;
import java.util.function.BiFunction;
// 매개변수 추가
public class MethodRefEx6 {
public static void main(String[] args) {
// 4. 임의 객체의 인스턴스 메서드 참조(특정 타입의)
Person person = new Person("Kim");
// 람다
BiFunction<Person, Integer, String> fun1 =
(Person p, Integer number) -> p.introduceWithNumber(number);
System.out.println("person.introduceWithNumber = " + fun1.apply(person, 1));
// 메서드 참조, 타입이 첫 번째 매개변수가 됨, 그리고 첫 번째 매개변수의 메서드를 호출
// 그리고 나머지는 순서대로 매개변수에 전달
BiFunction<Person, Integer, String> fun2 = Person::introduceWithNumber; // 타입::메서드명
System.out.println("person.introduceWithNumber = " + fun2.apply(person, 1));
}
}
BiFunction<Person, Integer, String> 인터페이스를 사용하여 (Person, Integer) -> String
형태의 람다/메서드 참조를 구현한다.
fun1 에서는 람다를 사용하여 p.introduceWithNumber(number) 를 호출한다.
fun2 에서는 Person::introduceWithNumber 라는 메서드 참조를 사용한다. 첫 번째 매개변수(Person) 가 메서드를 호출하는 객체가 되고, 두 번째 매개변수(Integer)가 introduceWithNumber() 의 실제 인자로 전달된다. 첫 번째 이후의 매개변수는 모두 순서대로 실제 인자로 전달된다.