메서드 참조가 필요한 이유
MethodRefStartV3.java
메서드 참조 사용 -> MethodRefStartV3::add
package methodref.start;
import java.util.function.BinaryOperator;
public class MethodRefStartV3 {
public static void main(String[] args) {
// BinaryOperator<Integer> add1 = (x, y) -> x + y;
// BinaryOperator<Integer> add1 = (x, y) -> add(x, y);
BinaryOperator<Integer> add1 = MethodRefStartV3::add; // 메서드 참조 사용
BinaryOperator<Integer> add2 = MethodRefStartV3::add;
Integer result1 = add1.apply(1, 2);
System.out.println(result1);
Integer result2 = add2.apply(1, 2);
System.out.println(result2);
}
static int add(int x, int y) {
return x + y;
}
}
메서드 참조(Method Reference)
: 이미 정의된 메서드를 그대로 참조해 람다 표현식을 더 간결하게 작성하는 방법
: (x, y) -> add(x, y)라는 람다는 사실 매개변수 x와 y를 add 메서드에 전달하기만 하는 코드이므로, 클래스명::메서드명 형태로 간단히 표현할 수 있음
: 코드가 더욱 간결해지고 가독성이 향상됨 -> 컴파일러가 자동으로 매개변수 매칭
: 람다를 작성할 때, 이미 정의된 메서드를 그대로 호출하는 경우 메서드 참조를 통해 더욱 직관적이고 간결한 코드 작성 가능
메서드 참조 1 - 시작
(x, y) -> 클래스명.메서드명(x, y) // 기존 람다
클래스명::메서드명 // 메서드 참조
-> 람다와 메서드 참조는 동등하게 동작한다.
-> 메서드 참조는 람다가 단순히 어떤 메서드만 호출하는 경우, 이를 축약해주는 문법이라고 이해하면 된다.
메서드 참조의 4가지 유형
1. 정적 메서드 참조
: 이름 그대로 static 메서드 참조
: 클래스명::메서드명
: ex) Math::max, Integer::paresInt 등
2. 특정 객체의 인스턴스 메서드 참조
: 특정 객체의 인스턴스 메서드 참조
: 객체명::인스턴스메서드명
: ex) person::introduce, person::getName
3. 생성자 참조
: 생성자 참조
: 클래스명::new
: ex) Person::new
4. 임의 객체의 인스턴스 메서드 참조
: 첫 번째 매개변수가 그 메서드를 호출하는 객체가 됨
: 클래스명::인스턴스메서드명
: Person::introduce, 같은 람다는 (Person p) -> p.introduce()
Person.java
package methodref;
public class Person {
private String name;
public Person() {
this("Unknown");
}
public Person(String name) {
this.name = name;
}
// 정적 메서드
public static String greeting() {
return "Hello";
}
// 정적 메서드, 매개변수
public static String greetingWithName(String name) {
return "Hello " + name;
}
public String getName() {
return name;
}
// 인스턴스 메서드
public String introduce() {
return "I am " + name;
}
// 인스턴스 메서드, 매개변수
public String introduceWithNumber(int number) {
return "I am " + name + ", my number is " + number;
}
}
MethodRefEx1.java
: 메서드 참조 유형 1~3번에 대해 알아보자
package methodref;
import java.util.function.Supplier;
public class MethodRefEx1 {
public static void main(String[] args) {
// 1. 정적 메서드 참조
Supplier<String> staticMethod1 = () -> Person.greeting();
System.out.println(staticMethod1.get()); // Hello 출력
Supplier<String> staticMethod2 = Person::greeting; // 클래스::정적메서드
System.out.println(staticMethod2.get()); // Hello 출력
// 2. 특정 객체으 인스턴스 참조
Person person = new Person("coco");
Supplier<String> instanceMethod1 = () -> person.introduce();
System.out.println(instanceMethod1.get()); // I am coco 출력
Supplier<String> instanceMethod2 = person::introduce; // 객체::인스턴스메서드
System.out.println(instanceMethod2.get()); // I am coco 출력
// 3. 생성자 참조
Supplier<Person> newPerson1 = () -> new Person();
System.out.println(newPerson1.get()); // methodref.Person@4411d970 출력
Supplier<Person> newPerson2 = Person::new;
System.out.println(newPerson2.get()); // methodref.Person@4411d970 출력
}
}
* 메서드 참조에서 ()를 사용하지 않는 이유
()는 메서드를 즉시 호출한다는 의미를 가짐, 여기서 ()가 없는 것은 메서드 참조를 하는 시점에는 메서드를 호출하는게 아니라 단순히 메서드의 이름으로 해당 메서드를 참조만 한다는 뜻
-> 해당 메서드의 실제 호출 시점은 함수형 인터페이스를 통해 이후 이루어짐
메서드 참조 2 - 매개변수
MethodRefEx2.java
: 매개변수가 존재하는 경우에 메서드를 어떻게 참조하는지 알아보자
package methodref;
import java.util.function.Function;
import java.util.function.Supplier;
public class MethodRefEx2 {
public static void main(String[] args) {
// 1. 정적 메서드 참조
Function<String, String> staticMethod1 = (name) -> Person.greetingWithName(name);
System.out.println(staticMethod1.apply("coco")); // Hello coco 출력
Function<String, String> staticMethod2 = Person::greetingWithName; // 클래스::정적메서드
System.out.println(staticMethod2.apply("coco")); // Hello coco 출력
// 2. 특정 객체으 인스턴스 참조
Person person = new Person("coco");
Function<Integer, String> instanceMethod1 = (n) -> person.introduceWithNumber(n);
System.out.println(instanceMethod1.apply(1)); // I am coco, my number is 1 출력
Function<Integer, String> instanceMethod2 = person::introduceWithNumber; // 객체::인스턴스메서드
System.out.println(instanceMethod2.apply(1)); // I am coco, my number is 1 출력
// 3. 생성자 참조
Function<String, Person> newPerson1 = (name) -> new Person(name);
System.out.println(newPerson1.apply("coco")); // methodref.Person@4411d970 출력
Function<String, Person> newPerson2 = Person::new;
System.out.println(newPerson2.apply("coco")); // methodref.Person@4411d970 출력
}
}
-> 메서드 참조에서는 매개변수를 생략함
-> 함수형 인터페이스의 시그니처(매개변수와 반환 타입)가 이미 정해져 있고, 컴파일러가 그 시그니처를 바탕으로 메서드 참조와 연결해 주기 때문에 명시적으로 매개변수를 작성하지 않아도 자동으로 추론되어 호출됨
메서드 참조 3 - 임의 객체의 인스턴스 메서드 참조
마지막 4번째 유형인 '임의 객체의 인스턴스 메서드 참조'에 대해 알아보자
MethodRefEx3.java
package methodref;
import java.util.function.Function;
public class MethodRefEx3 {
public static void main(String[] args) {
// 4. 임의 객체의 인스턴스 메서드 참조(특정 타입의)
Person person1 = new Person("kim");
Person person2 = new Person("park");
Person person3 = new Person("lee");
// 람다
Function<Person, String> func1 = (Person person) -> person.introduce();
System.out.println("person1.introduce = " + func1.apply(person1));
System.out.println("person2.introduce = " + func1.apply(person2));
System.out.println("person2.introduce = " + func1.apply(person3));
// 메서드 참조, 타입이 첫 번째 매개변수가 됨,
// 그리고 첫번째 매개변수의 메서드를 호출, 나머지는 순서대로 매개변수에 전달
Function<Person, String> func2 = Person::introduce; // 타입::인스턴스 메서드
System.out.println("person1.introduce = " + func2.apply(person1));
System.out.println("person2.introduce = " + func2.apply(person2));
System.out.println("person3.introduce = " + func2.apply(person3));
}
}
임의 객체의 인스턴스 메서드 참조
-> 클래스명::인스턴스 메서드
Person::introduce
1. 왼쪽에 지정한 클래스를 람다의 첫 번째 매개변수로 사용한다.
(Person person)
2. 오른쪽에 지정한 '인스턴스 메서드'를 첫 번째 매개변수를 통해 호출한다.
(Person person) -> person.introduce()
정리
1. 정적 메서드 참조 -> 클래스명::클레스메서드
2. 특정 객체의 인스턴스 메서드 참조 -> 객체명::인스턴스메서드
3. 생성자 참조 -> 클래스명::new(Person::new)
4. 임의 객체의 인스턴스 메서드 참조 -> 클래스명::인스턴스메서드
2번과 4번의 차이 !!!
2. 특정 객체의 인스턴스 메서드 참조
person::introduce // 메서드 참조: 인스턴스 person을 지정한다.
() -> person.introduce() // 람다: 지정한 person의 인스턴스 메서드를 사용한다.
// 실행 시점: 이미 지정된 인스턴스가 사용된다.
instanceMethod1.get()
-> 특정 객체의 인스턴스 메서드 참조는 선언 시점부터 이미 인스턴스가 지정되어 있음
-> 람다를 실행하는 시점에 인스턴스를 변경할 수 없음
4. 임의 객체의 인스턴스 메서드 참조
Person::introduce // 메서드 참조: Person이라는 타입만 지정한다. 어떤 인스턴스가 사용될지는 아직 모른다.
(Person person) -> person.introduce() // 람다: 매개변수로 넘어오는 person 인스턴스의 메서드를 사용
// 실행 시점: 실행 시점에 인스턴스를 외부에서 전달해서 변경할 수 있다.
fun1.apply(person1)
fun1.apply(person2)
fun1.apply(person3)
-> 선언 시점에 호출할 인스턴스를 지정하지 않음
-> 호출 대상을 매개변수로 선언해두고, 실행 시점이 되야 어떤 객체가 호출되는지 알 수 있음
메서드 참조 4 - 활용
MethodRefEx4.java
package methodref;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class MethodRef4 {
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());
System.out.println(result1); // [I am kim, I am park, I am lee] 출력
List<String> result2 = mapPersonToString(personList, Person::introduce);
System.out.println(result2); // // [I am kim, I am park, I am lee] 출력
List<String> upperResult1 = mapStringToString(result1, s -> s.toUpperCase());
System.out.println(upperResult1); // [I AM KIM, I AM PARK, I AM LEE] 출력
List<String> upperResult2 = mapStringToString(result1, String::toUpperCase);
System.out.println(upperResult2); // [I AM KIM, I AM PARK, I AM LEE] 출력
}
static List<String> mapPersonToString(List<Person> personList, Function<Person, String> func) {
List<String> result = new ArrayList<>();
for(Person p : personList) {
result.add(func.apply(p));
}
return result;
}
static List<String> mapStringToString(List<String> strings, Function<String, String> func) {
List<String> result = new ArrayList<>();
for(String s : strings) {
result.add(func.apply(s));
}
return result;
}
}
-> 임의 객체 메서드 참조를 사용해 코드가 더 간결해지고 의도가 명확히 드러난다.
매개변수가 있을 경우
// 람다 사용
BiFunction<Person, Integer, String> fun1 = (Person p, Integer number) -> p.introduceWithNumber(number);
// 메서드 참조 사용
BiFunction<Person, Integer, String> fun2 = Person::introduceWithNumber;
-> 첫 번째 인자를 호출 대상 객체로,
-> 나머지 인자들은 순서대로 해당 메서드의 매개변수로 전달
'인프런 > 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍' 카테고리의 다른 글
| [인프런] 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍 / 9. 스트림 API 2 - 기능 (0) | 2025.06.17 |
|---|---|
| [인프런] 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍 / 8. 스트림 API 1 - 기본 (3) | 2025.06.14 |
| [인프런] 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍 / 6. 람다 vs 익명 클래스 (0) | 2025.05.28 |
| [인프런] 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍 / 5. 람다 활용 (0) | 2025.05.22 |
| [인프런] 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍 / 4. 함수형 인터페이스 (0) | 2025.05.21 |