본문 바로가기
JAVA/Java

[Java] 메서드 참조(Method Reference)

by 민트맛녹차 2022. 9. 15.

메서드 참조란 이미 정의되어 있는 메서드를 사용해 람다식을 대신하는 것을 의미한다. 메서드 참조의 유형은 정적 메서드 참조, 인스턴스 메서드 참조, 생성자 참조로 크게 3가지가 있다. 인스턴스 메서드 참조는 참조를 통한 인스턴스 메서드 참조와 클래스 이름을 통한 인스턴스 메서드 참조로 구분할 수 있다.

 

정적(static) 메서드 참조

List<Integer> list = new ArrayList<>(Arrays.asList(1, 3, 5, 7, 9));
// public static void reverse(List<?> list)
Consumer<List<Integer>> c = l -> Collections.reverse(l);	// 람다식
c.accept(list)

Consumer<List<Integer>> c = Collections::reverse;		// 메서드 참조
c.accept(list)

메서드 참조를 통해 람다식을 작성할 필요없이 메서드 정보만 전달할 수 있다. static 메서드 참조는ClassName::staticMethodName 과 같이 참조한다. 

메서드 참조에서 람다식에 있는 인자 전달에 대한 정보를 생략할 수 있다. 위의 코드의 경우는 accept 호출 시 전달되는 인자를 reverse 메서드를 호출하면서 그대로 전달한다. 

 

인스턴스 메서드 참조 

static 메서드처럼 인스턴스 메서드도 참조할 수 있다.

참조변수 통한 인스턴스 메서드 참조

class JustSort {
    public void sort(List<?> list) { ... }
}

List<Integer> list = new ArrayList<>(Arrays.asList(1, 3, 5, 7, 9));
JustSort js = new JustSort();

Consumer<List<Integer>> c = e -> js.sort(e);	// 람다
c.accept(list)

Consumer<List<Integer>> c = js::sort;		// 메서드 참조
c.accept(list)

람다식에서 접근 가능한 참조변수는 final로 선언되었거나 effectively final이어야 한다. effectively final이란 참조하는 대상을 수정하지 않아 사실상 final 선언이 된 것과 다름없음을 의미한다. 람다식으로 생성된 인스턴스 내에서 final로 선언되지 않았거나 effectively final이 아닌 참조변수를 참조하는 것은 논리적 혼란을 일으키거나 예측 불가능한 상황으로 이어질 수 있어 이러한 제한을 둔다. 

인스턴스 메서드는 ReferenceName::instanceMethod 와 같이 참조한다. 

List<String> list = Arrays.asList("Box", "Robot");
list.forEach(s -> System.out.println(s));	// 람다
list.forEach(System.out::println);		// 메서드 참조

대표적인 예로 forEach 메서드가 있다. List 클래스의 forEach 메서드는 인자로 Consumer<? super T> 클래스를 받는다. 이때 System.out은 PrintStream 인스턴스를 참조하는 참소 변수이므로 위의 코드처럼 나타낼 수 있다. 

 

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

Class IBox {
    ....
    public int larger(IBox b) { ... }
}

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

ToIntBiFunction<IBox, IBox> bf = IBox::larger;			// 메서드 참조
int bigNum = bf.applyAsInt(ib1, ib2);

호출하는 메서드가 첫 번째 인스턴스의 메서드라면 인스턴스 생성 없이 인스턴스 메서드의 참조가 가능하다. bf.applyAsInt(ib1, ib2)를 호출한다면 아래와 같은 코드가 실행된다. (실제 코드가 아닌 실행되는 내용 표현)

bf.applyAsInt(ib1, ib2) {
    ib1.larger(ib2);
}

인스턴스 메서드는 ClassName::instanceMethodName 과 같이 참조한다.

 

생성자 참조

char[] src = {'R', 'o', 'b', 'o', 't'};

Function<char[], String> f = ar -> {
    return new String(ar);
};
String str = f.apply(src);

Function<char[], String> f = String::new
String str = f.apply(src);

인스턴스를 생성하고 이의 참조 값을 반환해야 하는 경우에도 메서드 참조를 사용할 수 있다. 람다식을 이루는 문장이 단순히 인스턴스의 생성 및 참조 값의 반환일 경우 메서드 참조를 사용할 수 있다.

위의 코드를 보면 f의 자료형이 Functional<char[], String> 이므로 매개변수 형이 char[]인 public String(char[] value) 생성자를 참조하게 된다.

생성자는 ClassName::new  와 같이 참조한다.

 

 

참조
윤성우의 열혈 JAVA 프로그래밍

댓글