[Java] 메소드 참조

✨New Wisdom✨·2020년 12월 23일
1

📕 Java 📕

목록 보기
9/24
post-thumbnail

메소드 참조

람다식은 결국 메소드 정의이다!

static 메소드의 참조

class ArrangeList {
    public static void main(String[] args) {
        List<Integer> ls = Arrays.asList(1, 3, 5, 7, 9);
        ls = new ArrayList<>(ls);

        Consumer<List<Integer>> c = l -> Collections.reverse(l); // 람다식
        c.accept(ls); // 순서 뒤집기 진행
        System.out.println(ls); // 출력
    }
}

public static void reverse(List<?> list) 이 static 메소드를 사용하는 것에 큰 의미가 있으므로,
자바 8에서 부터는 람다식 작성 필요 없이 다음과 같이 메소드 정보만 전달할 수 있다.

Consumer<List<Integer>> c = Collections::reverse;

이렇게 메소드 참조에서 람다식에 있는 인자 전달에 대한 정보를 생략할 수 있는 이유는
accept 메소드를 호출 시 전달되는 인자를 reverse 메소드를 호출하면서 그대로 전달하기 때문이다.

참조변수를 통한 인스턴스 메소드 참조

class JustSort {
    public void sort(List<?> lst) {    // 인스턴스 메소드
        Collections.reverse(lst);
    }
}

class ArrangeList3 {
    public static void main(String[] args) {
        List<Integer> ls = Arrays.asList(1, 3, 5, 7, 9);
        ls = new ArrayList<>(ls);
        JustSort js = new JustSort();

        Consumer<List<Integer>> c = e -> js.sort(e); // 람다식 기반
        c.accept(ls);
        System.out.println(ls);
    }
}

람다식에서 같은 지역 내에 선언된 참조변수 js에 접근하고 있다.
람다식이 인스턴스 생성으로 이어지지만, 람다식에서 접근 가능한 참조변수인 final로 선언되었거나 effectively final이면 같은 지역 내 선언된 참조변수에 접근할 수 있다.

🤔 effectively final이란?
사실상 final 선언이 된 것과 다름 없음을 뜻한다.
위의 예제에서 참조변수 js는 참조하는 대상을 수정하지 않았기 때문에
"사실상 final 선언이 된 것과 같다."

위 예제에서 등장한 람다식은 다음과 같은 메소드 참조로 변경할 수 있다.

Consumer<List<Integer>> c = js::sort;

인스턴스 메소드의 참조가 사용되는 실질적 예

import java.util.List;
import java.util.Arrays;

class ForEachDemo {

    public static void main(String[] args) {
        List<String> ls = Arrays.asList("Box", "Robot");
        
        // 람다식 기반
        ls.forEach(s -> System.out.println(s));

        // 메소드 참조 기반
        ls.forEach(System.out::println);
    }
}

Collection<E> 인터페이스는 Iterable<T>를 상속한다.
Iterable<T> 인터페이스에는 다음 디폴트 메소드가 정의되어 있다.

default void forEach(Consumer<? super T> action){
	for (T t : this) // this = 이 메소드가 속한 컬렉션 인스턴스
    	action.accept(t); // 모든 저장된 데이터들에 대해 이 문장 반복
}

forEach 메소드 호출을 위해서 Consumer<T> 인터페이스에 대한 람다식 또는 메소드 참조를 전달해야 하는데 추상 메소드인 accept는 결과를 반환하지 않고 전달된 인자를 대상으로 어떤 결과를 보인다.
따라서 System.out.println 메소드를 사용하기 적합!

클래스 이름을 통한 인스턴스 메소드 참조

import java.util.function.ToIntBiFunction;

class IBox {
    private int n; 

    public IBox(int i) { n = i; }

    public int larger(IBox b) {
        if(n > b.n)
            return n;
        else
            return b.n;
    }
}

class NoObjectMethodRef {
    public static void main(String[] args) {
        IBox ib1 = new IBox(5);
        IBox ib2 = new IBox(7);

        // 두 상자에 저장된 값 비교하여 더 큰 값을 반환
        ToIntBiFunction<IBox, IBox> bf = (b1, b2) -> b1.larger(b2);
        int bigNum = bf.applyAsInt(ib1, ib2);
        System.out.println(bigNum);}
}

// ToIntBiFunction<T, U>	int applyAsInt(T t, U u)

위 람다식에서 호출하는 메소드 larger가 첫번째 인자로 전달된 인스턴스의 메소드이며 다음과 같이 메소드 참조로 변환할 수 있다.

ToIntBiFunction<IBox, IBox> bf = IBox::larger;
// 클래스 이름::인스턴스 메소드 이름

생성자 참조

람다식을 작성하다 보면 인스턴스를 생성하고 이의 참조 값을 반환해야 하는 경우가 있는데
이때 메소드 참조 방식을 사용할 수 있다.

interface SMaker {
    String make(char[] ar);
}

class StringMaker {
    public static String chsToString(char[] a, SMaker m) {
        return m.make(a);
    }

    public static void main(String[] args) {
        SMaker sm = (ar) -> {
            return new String(ar);
        };
        
        char[] src = {'R', 'o', 'b', 'o', 't'};        
        String str = chsToString(src, sm);
        System.out.println(str);
    }
}

String 클래스의 public String(char[] value) 생성자를 이용한 String 인스턴스의 생성을 보이고 있다.

위의 람다식은 이렇게 줄일 수 있다.

Function<char[], String> f = ar -> new String(ar);

이는 메소드 참조방식으로 변경할 수 있다.

Funtion<char[], String> f = String::new;
// 클래스 이름::new
profile
🚛 블로그 이사합니다 https://newwisdom.tistory.com/

0개의 댓글