본문 바로가기

언어/Java

메소드 참조 (Method Reference)

메소드 참조의 4가지 유형과 메소드 참조의 장점

 

1. static 메소드의 참조

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

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

4. 생성자 참조

 

기본적으로 람다식보다 조금 더 코드를 단순하게 할 수 있다는 장점이 있다.

(특정 경우에 대해서만 람다식을 조금 더 줄여쓸 수 있음)

일부 람다식을 메소드 참조로 대신하게 할 수 있다.

 

 

static 메소드의 참조: 람다식 기반 예제

 

class ArrangeList {
	public static void main(String[] args) {
    	List<Integer> ls = Arrays.asList(1,3,5,7,9);
        ls = new ArrayList<>(ls);  // 타입인자 Integer 전달됨
        
        Consumer<List<Integer>> c = l -> Collections.reverse(l);
        c.accept(ls);  // 순서 뒤집기 진행
        System.out.println(ls);
    }
}

 

Consumver<T> 에는 void accept(T t)가 정의되어 있고

accept메소드를 어떻게 정의하든 이는 사용자의 마음이다.

 

static 메소드의 참조: 메소드 참조 기반으로 수정

 

 

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

accept 메소드 호출시 전달되는 인자를 reverse 메소드를 호출하며 그대로 전달한다는 약속에 근거한 수정이다.

이러면 static 메소드 Collections.reverse를 참조하여 Consumer.accept()를 호출하여 사용할 수 있게 된다.

 

 

인스턴스 메소드 참조 1: effectively final

 

 

effectivelry final 은 사실상 상수라는 의미이다.

아래 코드에서 js가 참조하는 인스턴스가 바뀌지 않으므로 이는 사실상 상수이므로 즉, effectively final이다.

 

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);  // private 정적 클래스 ArrayList 리턴
        ls = new ArrayList<>(ls); // 동적 ArrayList 로 변경
        JustSort js = new JustSort();  // js 는 effectively final
        
        Cousumer<List<Integer>> c = e -> js.sort(e);  // 람다식 기반
        c.accept(ls);
        System.out.println(ls);
    }
}

 

이 람다식 기반의 메소드 참조를 메소드 참조로 다음과 같이 수정할 수 있다.

이는 아래의 코드에서 js가 effectively final인 경우에만 허용이 된다.

 

js가 참조하는 인스턴스가 바뀌는 경우는 의도치 않은 다른 인스턴스의 메소드를 참조하는 경우가 생길 수 있으므로 위험하기 때문이다.

 

또는 js = null; 이라도 넣어주면 sort 메소드를 참조할 수조차 없게 된다.

 

이런 경우를 방지하기 위해 js는 effectively final 이어야 한다.

class JustSort {
	public void sort(List<?> lst) {
    	Collections.reverse(lst);
    }
}

class ArrangList3 {
	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 = js::sort;  // :: 기호는 컴파일러에 이것이 메소드 참조에 쓰일 것이라는 것도 전달해줌
        
        c.accept(ls); // 이는 이제 js 인스턴스의 sort 함수를 호출하여 ls를 인자로 주는 것과 같음
        System.out.println(ls);
    }
}

 

인스턴스 메소드 참조 forEach

 

class Iterable<T> {
	...
    default void forEach(Consumer<? super T> action) {
    	for (T t : this)  // this 는 이 메소드가 속한 컬렉션 인스턴스를 의미함
        	action.accept(t);
    ...
    }
}

// 이 때 Consumer<?>의 ?는 T의 상위 클래스이다.
// 즉, accept 메소드에서는 T 클래스 안에만 정의되어 있는 메소드는 사용불가
// 대신 ? 클래스의 참조 변수에 T 클래스의 인스턴스를 대입하여 사용 가능

class ForEachDemo {
	public static void main(String[] args) {
    	List<String> ls = Arrays.asList("Box", "Robot");
        // forEach 메소드를 쓰고 싶은데
        // forEach 메소드는 Consumer 인터페이스가 정의되길 원함
        // 즉, accept 메소드를 정의해줘야 Iterable 객체 안의 모든 요소에 대하여 accept 메소드 적용
        
        ls.forEach(t -> System.out.println(t));
        ls.forEach(System.out::println);
    }
}

 

 

인스턴스 메소드 참조 2:  인스턴스 없이 인스턴스 메소드 참조

 

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

ToIntBiFunction<T, U> int applyAsInt(T t, U u);  // 두 개의 인스턴스를 받아서 int를 리턴

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 을

ToIntBiFunction<IBox, IBox> bf = IBox::larger;

 

이렇게 줄일 수 있다.

 

 

생성자 참조

Function<T, R> R apply(T t)
// Function 인터페이스의 추상 메소드 apply는 T형 매개변수를 받아 R형 인스턴스를 리턴한다.

class StringMaker {
	public static void main(String[] args) {
    	Function<char[], String> f = ar -> new String(ar);
        char[] src = {'R', 'o', 'b', 'o', 't'};
        String str = f.apply(src);
        System.out.println(str);
    }
}

이 때 람다식을 다음과 같이 표현할 수 있다.

Function<char[], String> f = String::new;

'언어 > Java' 카테고리의 다른 글

스트림의 이해  (0) 2023.02.11
Optional 클래스  (0) 2023.02.11
정의되어 있는 함수형 인터페이스  (0) 2023.02.07
람다와 함수형 인터페이스  (0) 2023.02.07
람다(lambda)의 소개  (0) 2023.02.03