chapter 14
람다식에서 메소드 참조
- 람다식은 종종 기존 메소드를 단순히 호출만 하는 경우가 많은데, 메소드 참조(Method Reference)는 말 그대로 메소드를 참조해서 매개 변수의 정보 및 리턴 타입을 알아내어, 람다식에서 불필요한 매개 변수를 제거하는 것이 목적이다.
=> 예를 들어 두 개의 값을 받아 큰 수를 리턴하는 Math 클래스의 max() 정적 메소드를 호출하는 람다식은 다음과 같다.
(left, right) -> Math.max(left,right);
-> 이 때 람다식은 단순히 두 개의 값을 Math.max() 메소드의 매개값으로 전달하는 역할만 하기 때문에 다소 불편해보이는데, 이 경우에는 아래와 같이 메소드 참조를 이용하면 매우 깔끔하게 처리할 수 있다.
Math :: max; //메소드 참조
=>이게 바로 메소드 참조이다.
- 메소드 참조도 람다식과 마찬가지로 인터페이스의 익명 구현 객체로 생성되므로 타겟 타입인 인터페이스의 추상 메소드가 어떤 매개 변수를 가지고, 리턴 타입이 무엇인가에 따라 달라진다.
IntBinaryOperator operator = Math::max;
=> IntBinaryOperator 인터페이스는 추상메소드 applyAsInt()가 매개변수로 두개의 int, 리턴타입이 int이므로 Math::max 메소드 참조를 할 수 있다.
- 메소드 참조는 정적 또는 인스턴스 메소드를 참조할 수 있고, 생성자 참조도 가능하다.
정적 메소드와 인스턴스 메소드 참조
정적(static) 메소드를 참조할 경우에는 클래스 이름 뒤에 ::기호를 붙이고 정적 메소드 이름을 기술하면 된다.
클래스 :: 메소드
인스턴스(instance) 메소드일 경우에는 먼저 객체를 생성한 다음 참조 변수 뒤에 ::기호를 붙이고 인스턴스 메소드 이름을 기술하면 된다
참조변수 :: 메소드
import java.util.function.IntBinaryOperator;
public class MothdReferenceExample {
public static void main(String[] args) {
IntBinaryOperator operator;
//정적 메소드 참조
operator = (x,y) -> Calculator.staticMethod(x,y);
System.out.println("결과1: " +operator.applyAsInt(1,2));
operator = Calculator :: staticMethod;
System.out.println("결과2: " + operator.applyAsInt(1,2));
//인스턴스 메소드 참조
Calculator obj = new Calculator();
operator = (x,y) -> obj.instanceMethod(x,y);
System.out.println("결과1: " + operator.applyAsInt(1,2));
operator = obj ::instanceMethod;
System.out.println("결과2: " +operator.applyAsInt(1,2));
}
}
매개변수의 메소드 참조
=> 메소드는 람다식 외부의 클래스 멤버일 수도 있고, 람다식에서 제공되는 매개 변수의 멤버일 수도 있다. 이전 예제는 람다식 외부의 클래스 멤버인 메소드를 호출하였는데, 그러나 다음과 같이 람다식에서 제공되는 a 매개 변수의 메소드를 호출해서 b 매개 변수를 매개값으로 사용하는 경우도 있다.
(a,b)-> {a.instanceMethod(b); }
=> 이것을 메소드 참조로 표현하면 a의 클래스 이름 뒤에서 ::기호를 붙이고 메소드 이름을 기술하면 된다.
클래스 :: instanceMethod;
=> 작성방법은 정적 메소드 참조와 동일하지만, a의 인스턴스 메소드가 참조되므로 전혀 다른 코드가 실행된다.
public class ArgumetnMethodReferenceExample {
public static void main(String[] args) {
ToIntBiFunction<String,String> function;
function = (a,b)-> a.compareToIgnoreCase(b);
int order = function.applyAsInt("java8","JAVA8");
print(order);
function = String::compareToIgnoreCase;
order = function.applyAsInt("JAVA8", "java8");
print(order);
}
private static void print(int order){
if(order<0) {
System.out.println("사전 순으로 먼저 옵니다.");
}else if(order == 0){
System.out.println("동일한 문자열입니다.");
}else{
System.out.println("사전 순으로 나중에 나옵니다.");
}
}
}
=> 참고로, a.compareToIgnoreCase(b)로 호출될 때 사전 순으로 a가 b보다 먼저 나오면 음수, 동일하면 0을, 더 늦게 나오면 양수를 리턴한다.
생성자 참조
-
메소드 참조(method references)는 생성자 참조도 포함한다. 생성자를 참조한다는 것은 객체 생성을 의미한다.
-
단순히 메소드 호출로 구성된 람다식을 메소드 참조로 대치할 수 있듯이, 단순히 객체를 생성하고 리턴하도록 람다식은 생성자 참조로 대치할 수 있다.
(a,b) -> {return new 클래스(a,b);}
클래스::new //생성자 참조
=> 클래스 이름 뒤에 :: 기호를 붙이고 new 연산자를 기술하면 된다. 생성자가 오버로딩되어 여러 개가 있을 경우, 컴파일러는 함수적 인터페이스의 추상 메소드와 동일한 매개 변수 타입과 개수를 가지고 있는 생성자를 찾아 실행한다.
public class ConstructorReferenceExample {
public static void main(String[] args) {
Function<String,Member> function = Member::new;
Member member1 = function.apply("angel");
System.out.println(member1.getId());
System.out.println();
BiFunction<String,String,Member> function2 = Member::new;
Member member2= function2.apply("신천사","angel");
System.out.println(member2.getName());
System.out.println(member2.getId());
}
}
class Member {
private String name;
private String id;
Member(){
System.out.println("method() 실행");
}
Member(String id){
System.out.println("Member(String id) 실행");
this.id = id;
}
Member(String name, String id){
System.out.println("Member(String name, String id) 실행");
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public String getId() {
return id;
}
}