[이것이자바다] chapter 14. 람다식에서 메소드 참조

chapter 14

람다식에서 메소드 참조

=> 예를 들어 두 개의 값을 받아 큰 수를 리턴하는 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을, 더 늦게 나오면 양수를 리턴한다.

생성자 참조

(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;
    }
}