일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- 자바로 계산기 만들기
- 스프링 mvc1 - 스프링 mvc
- 자바의 정석 기초편 ch14
- 자바의 정석 기초편 ch13
- 자바의 정석 기초편 ch1
- 자바로 키오스크 만들기
- 자바 기초
- 자바 중급2편 - 컬렉션 프레임워크
- 자바 고급2편 - io
- 데이터 접근 기술
- 자바 중급1편 - 날짜와 시간
- 자바의 정석 기초편 ch7
- 2024 정보처리기사 시나공 필기
- 자바의 정석 기초편 ch5
- 스프링 트랜잭션
- 스프링 mvc2 - 검증
- 스프링 입문(무료)
- 자바의 정석 기초편 ch4
- 자바의 정석 기초편 ch9
- 2024 정보처리기사 수제비 실기
- 람다
- 자바의 정석 기초편 ch11
- 스프링 고급 - 스프링 aop
- 스프링 mvc2 - 타임리프
- @Aspect
- 자바의 정석 기초편 ch12
- 스프링 mvc2 - 로그인 처리
- 자바의 정석 기초편 ch2
- 자바 고급2편 - 네트워크 프로그램
- 자바의 정석 기초편 ch6
- Today
- Total
개발공부기록
메서드 참조가 필요한 이유, 메서드 참조(시작, 매개변수, 임의 객체의 인스턴스 메서드 참조, 활용) 본문
메서드 참조가 필요한 이유, 메서드 참조(시작, 매개변수, 임의 객체의 인스턴스 메서드 참조, 활용)
소소한나구리 2025. 6. 1. 17:35메서드 참조가 필요한 이유
메서드 참조(Method References) 알아보기
예제1
package methodref.start;
public class MethodRefStartV1 {
public static void main(String[] args) {
BinaryOperator<Integer> add1 = (x, y) -> x + y;
BinaryOperator<Integer> add2 = (x, y) -> x + y;
Integer result1 = add1.apply(1, 2);
System.out.println("result1 = " + result1);
Integer result2 = add2.apply(1, 2);
System.out.println("result2 = " + result2);
}
}
/* 실행 결과
result1 = 3
result2 = 3
*/
- 두 정수를 더하는 간단한 연산을 수행하는 동일한 람다를 add1, add2로 각각 정의하고 있는 예제이다
- 이렇게 동일한 기능을 하는 람다를 여러곳에서 계속 생성해서 사용하면 코드가 중복되어 유지보수가 어려워지고 덧셈 로직이 변경되면 모든 람다를 각각 수정해야 한다.
예제2
package methodref.start;
public class MethodRefStartV2 {
public static void main(String[] args) {
BinaryOperator<Integer> add1 = (x, y) -> add(x, y);
BinaryOperator<Integer> add2 = (x, y) -> add(x, y);
// 호출 및 출력 동일
}
private static int add(int x, int y) {
return x + y;
}
}
/* 결과 동일 */
- 이 예제에서는 동일안 람다식을 add() 메서드로 추출하여 람다에서는 add() 메서드를 호출한다.
- 로직이 한 곳으로 모여 유지보수가 쉬워졌으며 코드 중복 문제도 해결했다.
- 하지만 람다를 작성할 때 마다 (x, y) -> add(x, y) 형태의 코드를 반복해서 작성해야하며 매개변수를 전달하는 부분이 조금 장황하다.
예제3
package methodref.start;
public class MethodRefStartV3 {
public static void main(String[] args) {
// 메서드 참조
BinaryOperator<Integer> add1 = MethodRefStartV3::add;
BinaryOperator<Integer> add2 = MethodRefStartV3::add;
// add() 메서드 정의 및 호출, 출력 동일
}
/* 실행 결과 동일 */
- 메서드 참조(Method Reference)문법인 클래스명::메서드명을 사용하여 (x, y) -> add(x, y)라는 람다를 더욱 간단하게 표현하였으며 내부적으로 (x, y) -> add(x, y)와 동일하게 동작한다.
메서드 참조의 장점
- 메서드 참조를 사용하면 코드가 더욱 간결해지고 가독성이 향상되며 더 이상 매개변수를 명시적으로 작성할 필요가 없다.(컴파일러가 자동으로 매개변수를 매칭함)
- 별도의 로직 분리와 함께 재사용성 역시 높아지게 된다.
메서드 참조는 "이미 정의된 메서드를 그대로 참조하여 람다 표현식을 더 간결하게 작성하는 방법" 이라고 할 수 있다.
위의 예제처럼 (x, y) -> add(x, y)라는 람다는 사실상 매개변수 x, y를 그대로 add() 메서드에 전달하기만 하는 코드이기 때문에 클래스명::메서드명 형태의 메서드 참조로 간단히 표현할 수 있다.
이렇게 하면 불필요한 매개변수 선언 없이 코드가 깔끔해지고 가독성도 높아진다.
메서드 참조는 이미 정의된 메서드를 람다로 변환하여 더욱 간결하게 사용할 수 있도록 해주는 문법적 편의 기능으로 장황한 람다 대신 간단하고 매우 직관적으로 사용할 수 있다.
람다를 작성할 때 이미 정의된 메서드를 그대로 호출하는 경우(추가 로직이 없는 경우)라면 메서드 참조를 통해 더욱 직관적이고 간결한 코드를 작성할 수 있다.
메서드 참조
시작
람다 내부에서 단순히 어떤 메서드(정적/인스턴스/생성자 등)를 호출만하고 있는 경우에는 메서드 참조를 사용할 수 있으며 람다와 메서드 참조는 동등하게 동작한다
메서드 참조의 4가지 유형
- 정적 메서드 참조
- 이름 그대로 정적(static)메서드를 참조한다
- 문법: 클래스명::메서드명
- 예: Math::max, Integer::parseInt 등
- 특정 객체의 인스턴스 메서드 참조
- 특정 객체의 인스턴스 메서드를 참조한다
- 문법: 객체명::인스턴스메서드명
- 예: person::introduce, person::getName 등
- 생성자 참조
- 생성자를 참조한다
- 문법: 클래스명::new
- 예: Person::new 등
- 임의 객체의 인스턴스 메서드 참조
- 첫 번째 매개변수(또는 해당 람다가 받을 대상)가 그 메서드를 호출하는 객체가 된다
- 문법: 클래스명::인스턴스메서드명
- 예: Person::introduce, == (Person p) -> p.introduce()
매개변수가 없을 때 메서드 참조 예제
정적 메서드, 특정 객체의 인스턴스 메서드, 생성자를 메서드 참조한 예제이다
Person
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;
}
}
- 매개변수가 있거나 없는 생성자, 정적 메서드, 인스턴스 메서드들을 가지고 있는 예제를 위한 Person 객체이다
MethodRefEx1
package methodref;
public class MethodRefEx1 {
public static void main(String[] args) {
// 1. 정적 메서드 참조
Supplier<String> staticMethod1 = () -> Person.greeting();
Supplier<String> staticMethod2 = Person::greeting; // 클래스::정적메서드
System.out.println("staticMethod1 = " + staticMethod1.get());
System.out.println("staticMethod2 = " + staticMethod2.get());
// 2. 특정 객체의 인스턴스 참조
Person person = new Person("Kim");
Supplier<String> instanceMethod1 = () -> person.introduce();
Supplier<String> instanceMethod2 = person::introduce; // 객체::인스턴스메서드
System.out.println("instanceMethod1 = " + instanceMethod1.get());
System.out.println("instanceMethod2 = " + instanceMethod2.get());
// 3. 생성자 참조
Supplier<Person> newPerson1 = () -> new Person();
Supplier<Person> newPerson2 = Person::new; // 클래스::new
System.out.println("newPerson1 = " + newPerson1.get());
System.out.println("newPerson2 = " + newPerson2.get());
}
}
/* 실행 결과
staticMethod1 = Hello
staticMethod2 = Hello
instanceMethod1 = I am Kim
instanceMethod2 = I am Kim
newPerson1 = methodref.Person@4e50df2e
newPerson2 = methodref.Person@1d81eb93
*/
- () -> Person.greeting() ➡️ Person::greeting: 정적 메서드를 참조한 예시이다
- () -> person.introduce() ➡️ person::introduce: 특정 객체(Person)의 인스턴스 메서드를 참조한 예시이다.
- () -> new Person() ➡️ Person::new: 생성자를 참조한 예시이다
이렇게 이미 정의된 메서드를 호출하기만 하는 람다는 메서드 참조로 간단히 표현할 수 있다.
메서드 참조에서 ()를 사용하지 않는 이유
- 메서드 참조의 문법을 보면 메서드명 뒤에 ()가 없는것을 확인할 수 있는데, ()는 메서드를 즉시 호출한다는 의미를 가진다.
- 즉, 메서드 참조를 하는 시점에는 메서드를 호출하는것이 아니라 단순히 메서드의 이름으로 해당 메서드를 참조만 한다는 뜻으로 해당 메서드의 실제 호출 시점은 함수형 인터페이스를 통한 이후에 이루어진다.
매개변수가 있을 때 메서드 참조 예제
매개변수가 있는 정적 메서드, 특정 객체의 인스턴스 메서드, 생성자를 메서드 참조한 예제이다
MethodRefEx2
package methodref;
public class MethodRefEx2 {
public static void main(String[] args) {
// 1. 정적 메서드 참조
Function<String, String> staticMethod1 = name -> Person.greetingWithName(name);
Function<String, String> staticMethod2 = Person::greetingWithName;
System.out.println("staticMethod1 = " + staticMethod1.apply("kim"));
System.out.println("staticMethod2 = " + staticMethod2.apply("kim"));
// 2. 특정 객체의 인스턴스 참조
Person person = new Person("Kim");
Function<Integer, String> instanceMethod1 = n -> person.introduceWithNumber(n);
Function<Integer, String> instanceMethod2 = person::introduceWithNumber;
System.out.println("instanceMethod1 = " + instanceMethod1.apply(1));
System.out.println("instanceMethod2 = " + instanceMethod2.apply(1));
// 3. 생성자 참조
Function<String, Person> newPerson1 = name -> new Person(name);
Function<String, Person> newPerson2 = Person::new;
System.out.println("newPerson1 = " + newPerson1.apply("kim"));
System.out.println("newPerson2 = " + newPerson2.apply("kim"));
}
}
/* 실행 결과
staticMethod1 = Hellokim
staticMethod2 = Hellokim
instanceMethod1 = I am Kim, my number is 1
instanceMethod2 = I am Kim, my number is 1
newPerson1 = methodref.Person@1ddc4ec2
*/
- 매개변수가 있는 메서드 참조의 예시를 보면 매개변수가 없는 메서드 참조의 예시와 완전히 동일한 모양인 것을 확인할 수 있다
- 메서드 참조는 매개변수를 생략하기 때문인데 만약 매개변수가 여러개라면 순서대로 전달된다.
메서드 참조에서 매개변수를 생략하는 이유
- 함수형 인터페이스의 시그니처(매개변수와 반환 타입)가 이미 정해져 있고, 컴파일러가 그 시그니처를 바탕으로 메서드 참조와 연결해 주기 때문에 명시적으로 매개변수를 작성하지 않아도 자동으로 추론되어 호출된다
- 예를 들어 Function<String, String> 이라는 함수형 인터페이스는 입력과 출력에 String이라는 시그니처를 가지고 있기 때문에 Person::greetingWithName라고 메서드 참조를 하면 컴파일러가 Function<String, String>을 만족하려면 greetingWithName(String name)을 호출해야 한다고 판단하여 매개변수를 전달한다.
임의 객체의 인스턴스 메서드 참조
MethodRefEx3
package methodref;
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> fun1 = (Person person) -> person.introduce();
System.out.println("person1.introduce = " + fun1.apply(person1));
System.out.println("person2.introduce = " + fun1.apply(person2));
System.out.println("person3.introduce = " + fun1.apply(person3));
// 메서드 참조
// 타입이 첫 번째 매개변수가 됨, 그리고 첫 번째 매개변수의 메서드를 호출, 나머지는 순서대로 매개변수에 전달
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));
}
}
/* 실행 결과
person1.introduce = I am Kim
person2.introduce = I am Park
person3.introduce = I am Lee
person1.introduce = I am Kim
person2.introduce = I am Park
person3.introduce = I am Lee
*/
- Person 타입을 매개변수로 받고 넘겨받은 person인스턴스의 introduce() 인스턴스 메서드를 호출한다
- 람다를 실행하면 Function<Person, String> 함수형 인터페이스를 사용하기 때문에 Person 타입의 인스턴스를 인자로 받고 String을 반환한다.
- apply()에 person1, person2, person3을 각각 전달하면 각 인스턴스의 introduce()가 호출된다.
- 이 람다는 매개변수로 지정한 특정한 타입의 객체에 대해 동일한 메서드를 호출하는 패턴을 보인다
- 매개변수로 지정한 특정 타입: Person
- 메서드: introduce()
- 즉, 특정 타입의 임의 객체의 인스턴스 메서드를 참조하는데 위의 예제에서는 Person 타입을 구현한 어떠한 객체든 해당 람다에 전달할 수 있다.
- 이렇게 특정 타입의 임의 객체에 대해 동일한 인스턴스 메서드를 호출하는 패턴을 메서드 참조로 손쉽게 표현할 수 있다
위와 같은 메서드 참조를 특정 타입의 임의 객체의 인스턴스 참조(Reference to an instance method of an arbitrary object of a particular type)라고 공식 문서에 나와있는데 편의상 임의 객체의 인스턴스 참조라고도 한다고 한다.
임의 객체의 인스턴스 참조는 클래스명::인스턴스메서드와 같이 사용하며 왼쪽이 클래스명이고 오른쪽이 인스턴스 메서드이다
Person::introduce는 아래와 같은 람다가 된다
- 왼 쪽에 지정한 클래스(Person)를 람다의 첫 번째 매개변수로 사용한다
- (Person person)
- 그 다음 오른쪽에 지정한 인스턴스 메서드를 첫 번째 매개변수를 통해 호출한다
- (Person person) -> person.introduce()
메서드 참조 정리
- 정적 메서드 참조 - 클래스명::클래스메서드(Person::greeting)
- 특정 객체의 인스턴스 메서드 참조 - 객체명::인스턴스메서드(person::introduce)
- 생성자 참조 - 클래스명::new(Person::new)
- 임의 객체의 인스턴스 메서드 참조 - 클래스명::인스턴스메서드(Person::introduce)
여기서 2번과 4번이 조금 헷갈릴 수 있는데 두 메서드 참조는 문법과 기능이 전혀 다르다.
이둘의 차이를 조금 더 자세히 살펴보면 아래와 같다
2번(특정 객체의 인스턴스 메서드 참조)은 메서드 참조를 선언할 때부터 이름 그대로 특정 객체(인스턴스)를 지정해야 한다
- person::introduce: 인스턴스 person을 지정한다.
- () -> person.introduce(): 지정한 person의 인스턴스 메서드를 사용한다.
- 이 방식은 실행 시점에 이미 지정된 인스턴스가 사용된다
4번(임의 객체의 인스턴스 메서드 참조)는 메서드 참조를 선언할 때는 어떤 객체(인스턴스)가 대상이 될지 모른다
- Person::introduce: Person 이라는 타입만 지정하며 어떤 인스턴스가 사용될지는 아직 모른다
- (Person person) -> person.introduce(): 매개변수로 넘어오는 person 인스턴스의 메서드를 사용한다.
- 이 방식은 실행 시점에 인스턴스를 외부에서 전달해서 변경할 수 있다
- fun1.apply(person1)
- fun1.apply(person2)
- fun1.apply(person3)
임의 객체의 인스턴스 메서드 참조는 선언 시점에 호출할 인스턴스를 지정하지 않는 대신 호출 대상을 매개변수로 선언해두고 실행 시점에 호출할 인스턴스를 받는다
즉, 실행 시점이 되어야 어떤 객체가 호출되는지 알 수 있으므로 임의 객체의 인스턴스 메서드 참조라고 한다
둘의 핵심적인 차이는 메서드 참조나 람다를 정의하는 시점에 호출할 대상의 인스턴스가 고정되는 것인지, 아닌지에 달려있다.
- 특정 객체의 인스턴스 메서드 참조: 선언 시점에 메서드를 호출할 특정 객체가 고정된다
- 특정 타입의 임의 객체의 인스턴스 메서드 참조: 선언시점에 메서드를 호출할 특정 객체가 고정되지 않고 실행 시점에 인자로 넘긴 임의의 객체가 사용된다.
공식문서에서 말하는 특정 타입, 임의 객체의 단어를 풀어서 설명하면 아래와 같다
- 특정 타입: 타입은 선언 시에 특정한 타입으로 고정된다, 예제에서는 Person 타입으로 고정 했다
- 임의 객체: 메서드를 호출할 객체는 고정되지 않는다. Person 타입을 사용하는 임의 객체가 실행 시점에 사용될 수 있다.
복잡해 보이는 이 4번의 메서드 참조가 가장 많이 사용된다고 한다.
활용 예제
MethodRefEx4
package methodref;
public class MethodRefEx4 {
public static void main(String[] args) {
List<Person> persons = List.of(
new Person("Kim"),
new Person("Park"),
new Person("Lee")
);
List<String> result1 = mapPersonToString(persons, (Person person) -> person.introduce());
List<String> result2 = mapPersonToString(persons, 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(result2, 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 person : personList) {
String applied = fun.apply(person);
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;
}
}
/* 실행 결과
result1 = [I am Kim, I am Park, I am Lee]
result2 = [I am Kim, I am Park, I am Lee]
upperResult1 = [I AM KIM, I AM PARK, I AM LEE]
upperResult2 = [I AM KIM, I AM PARK, I AM LEE]
*/
- mapPersonToString 메서드
- Person 객체 리스트를 받아서 String 리스트로 변환하는 메서드로 변환 로직을 Function<Person, String>으로 받아 유연하게 처리한다.
- Person::introduce는 클래스::인스턴스메서드 이므로 임의 객체의 인스턴스 메서드 참조를 사용한 것이다
- 왼쪽에 지정한 Person이 첫 번째 매개변수가 되고 오른쪽에 지정한 인스턴스 메서드를 해당 매개변수가 호출한다
- fun.apply(person) 메서드에 List<Person> persons에 담긴 각각의 인스턴스가 인자로 넘어가면서 해당 인스턴스의 introduce() 메서드가 호출된다.
- mapStringToString
- String 리스트를 다른 형태의 String 리스트로 변환하는 메서드이며 입력값의 List가 Person에서 String으로 변경되는 것만 제외하면 위와 완전히 동일하다
- String::toUpperCase이므로 임의 객체의 인스턴스 메서드 참조를 사용한 것이며 매개변수로 넘어온 각각의 String 인스턴스가 fun.apply(s) 메서드의 인자로 넘어가면서 해당 인스턴스의 toUpperCase() 메서드가 호출된다
MethodRefEx5
package methodref;
public class MethodRefEx5 {
public static void main(String[] args) {
List<Person> persons = List.of(
new Person("Kim"),
new Person("Park"),
new Person("Lee")
);
List<String> result1 = MyStreamV3.of(persons)
.map(person -> person.introduce())
.map(str -> str.toUpperCase())
.toList();
System.out.println("result1 = " + result1);
List<String> result2 = MyStreamV3.of(persons)
.map(Person::introduce)
.map(String::toUpperCase)
.toList();
System.out.println("result2 = " + result2);
}
}
/* 실행 결과
result1 = [I AM KIM, I AM PARK, I AM LEE]
result2 = [I AM KIM, I AM PARK, I AM LEE]
*/
- 기존에 만들었던 MyStreamV3을 활용하여 스트림에 메서드 참조를 활용하는 예제이다.
- result1은 람다를 사용하여 각 Person 객체에 대해 introduce() 메서드를 호출한 후 결과 문자열을 모두 대문자로 변환한다.
- result2는 동일한 처리 과정을 메서드 참조로 간략하게 표현했다.
람다 대신에 메서드 참조를 사용한 코드를 비교해보면 코드가 더 간결해지고 의도가 더 명확하게 드러나는 것을 확인할 수 있다.
람다로도 충분히 표현할 수 있지만 내부적으로 호출만 하는 간단한 람다라면 메서드 참조가 더 짧고 명확하게 표현될 수 있으며 이런 방식은 코드 가독성을 높이는 장점이 있다.
임의 객체의 인스턴스 메서드 참조에서 매개변수가 늘어나면?
MethodRefEx6
package methodref;
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("fun1.introduceWithNumber = " + fun1.apply(person, 1));
// 메서드 참조, 타입::인스턴스메서드명
// 타입이 첫 번째 매개변수가 되고 첫 번째 매개변수의 메서드를 호출
// 나머지는 순서대로 매개변수에 전달
BiFunction<Person, Integer, String> fun2 = Person::introduceWithNumber;
System.out.println("fun2.introduceWithNumber = " + fun2.apply(person, 1));
}
}
/* 실행 결과
fun1.introduceWithNumber = I am Kim, my number is 1
fun2.introduceWithNumber = I am Kim, my number is 1
*/
- BiFunction<Person, Integer, String> 인터페이스를 사용하여 (Person, Integer) -> String 형태의 람다/메서드 참조를 구현한다.
- fun1에서는 람다를 사용하여 p.introduceWithNumber(number)를 호출하고 fun2에서는 Person::introduceWithNumber라는 메서드 참조를 사용한다.
- 첫 번째 매개변수(Person)가 메서드를 호출하는 객체가 되고 두 번째 매개변수(Integer)가 introduceWithNumber() 메서드의 실제 인자로 전달된다
- 첫 번째 이후의 매개변수는 모두 순서대로 실제 인자로 전달된다.
이처럼 임의 객체의 인스턴스 메서드 참조는 함수형 인터페이스의 시그니처에 따라 첫 번째 인자를 호출 대상 객체로, 나머지 인자들은 순서대로 해당 메서드의 매개변수로 전달된다.
정리
메서드 참조의 필요성
- 람다에서 이미 정의된 메서드를 단순히 호출하기만 하는 경우 메서드 참조로 더 간결하게 표현할 수 있다.
- 코드 중복을 줄이고 가독성을 높여주며 유지보수 측면에서도 편리하다.
1. 간결성
- 람다 표현식을 더욱 간단하게 표현할 수 있다
- 매개변수를 생략한다
- 특히 메서드 체이닝에서 코드가 깔끔해진다
2. 가독성
- 메서드 이름을 직접 사용하여 의도가 더 명확해진다
- 복잡한 람다 표현식을 단순화 할 수 있다.
3. 유연성
- 다양한 함수형 인터페이스와 함께 사용할 수 있다.
- 스트림 API와 결합하여 강력한 데이터 처리가 가능하다
4. 재사용성
- 기존 메서드를 람다 표현식으로 쉽게 변환할 수 있다.
- 동일한 메서드를 여러 컨텍스트에서 재사용할 수 있다.
메서드 참조의 4가지 유형
- 정적 메서드 참조 - 클래스명::메서드명
- 특정 객체의 인스턴스 메서드 참조 - 객체명::메서드명
- 생성자 참조 - 클래스명::new
- 임의 객체의 인스턴스 메서드 참조 - 클래스명::인스턴스메서드명
- 클래스명으로 지정한 첫 번째 매개변수가 호출 대상 객체가 됨
- 첫 번째 이후의 매개변수는 메서드의 인자로 전달됨
임의 객체의 인스턴스 메서드 참조
- 특정 타입의 객체가 런타임에 주어지면 그 객체를 사용해 인스턴스 메서드를 호출하는 방식을 간단하게 표현함
- (Person p) -> p.introduce 대신 Person::introduce로 표기됨
활용 예시
- 컬렉션(List, Map 등)의 원소들을 변환(map)할 때나 스트림(Stream)에서 map, filter 등을 사용할 때 이미 정의된 메서드를 그대로 쓸 수 있다면 메서드 참조를 사용할 때 더욱 깔끔하고 직관적인 코드를 작성할 수 있다.
람다와의 관계
- 메서드 참조는 람다를 메서드 호출만으로 축약한 문법이며 내부 동작은 사실상 동일하다.
- 람다로 표현하기에 직관적인 경우에는 람다를 사용하면 되지만 메서드 참조가 더 간결하고 읽기 쉬운 경우에는 메서드 참조를 사용하면 된다.
메서드 참조는 자바 코드의 간결함과 가독성을 향상시켜주는 매우 유용한 기능이므로 여러 상황에서 적극적으로 활용하는 것을 추천한다.
다만, 익숙해지는데 어느정도의 시간이 필요하다.
출처 : 인프런 - 김영한의 실전 자바 - 고급3편 (유료) / 김영한님
유료 강의이므로 정리에 초점을 두고 코드는 일부만 인용함
'자바 로드맵 강의 > 고급 3 - 람다, 스트림, 함수형 프로그래밍' 카테고리의 다른 글
스트림 API - 기본, 스트림 API 시작, 스트림 API란?, 파이프라인 구성, 지연 연산, 지연 연산과 최적화 (5) | 2025.06.03 |
---|---|
람다 vs 익명 클래스 (1) | 2025.05.25 |
람다 활용, 필터 만들기, 맵 만들기, 필터와 맵 활용, 스트림 만들기 (1) | 2025.04.21 |
함수형 인터페이스, 함수형 인터페이스와 제네릭, 람다와 타겟 타입, 함수형 인터페이스(기본, 특화, 기타) (0) | 2025.04.04 |
람다, 람다 정의, 함수형 인터페이스, 람다와 시그니처, 람다와 생략, 람다의 전달, 고차 함수 (0) | 2025.04.03 |