메서드 레퍼런스
- 메서드 레퍼런스는 이름과 동일하게 메서드를 대상으로 한 람다식의 간략형이며, 메서드 참조를 나타내는 예약어로서 (::)를 사용한다. 다시 말해서 메서드 레퍼런스는 말 그대로 메소드를 참조해서 매개 변수의 정보 및 리턴 타입을 알아내어, 람다식에서 불 필요한 매개 변수를 제거하는 것이 목적이다.
- 메소드 참조의 예와 그에 대한 람다식은 다음과 같다.
기본 구조
String::valueOf x -> String.valueOf(x)
Object::toString x -> x.toString()
x::toString () -> x.toString()
왼쪽은 메서드 레퍼런스이고 오른쪽은 람다식이다.
메서드 레퍼런스의 3가지 형태
:: 연산자는 객체 또는 클래스와 메서드 이름을 구분하며, 세 가지의 경우로 사용된다.
1. Object::instaceMethod
text -> System.out.println(text);
2. Class::staticMethod
정적(static) 메소드를 참조할 경우에는 클래스 이름 뒤에 :: 기호를 붙이고 정적 메소드 이름을 기술하면된다.
클래스 :: 메소드
예를 들어 두 개의 값을 받아 큰 수를 리턴하는 Math 클래스의 max() 정적 메소드를 호출하는 람다식은다음과 같다.
(left, right) -> Math.max(left, right);
람다식은 단순히 두 개의 값을 Math.max() 메소드의 매개값으로 전달하는 역할만 하기 때문에 다소 불편하다.
그래서 메소드 레퍼런스를 사용하여 깔끔하게 처리할 수 있다.
Math :: max;
3. Class::instanceMethod
인스턴스 메소드일 경우에는 먼저 객체를 생성한 다음 참조 변수 뒤에 :: 기호를 붙이고 인스턴스 메소드 이름을 기술하면 된다.
예를 들면
1 2 3 4 5 6 7 8 9 10 11 12 | Class Greeter { Public void greet() { System.out.println("Hello, world!"); } } Class ConcurrentGreeter extends Greeter { Public void greet() { Thread t = new Thread(super::greet); t.start(); } } | cs |
2,3의 사용 예제
IntBinaryOperator 인터페이스는 두 개의 int 매개값을 받아 int 값을 리턴하므로 Math :: max 메소드 참조를 대입할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | // static wedulObject 클래스 public class WedulObject { public static int staticMethod(int x, int y) { return x + y; } public int instanceMethod(int x, int y) { return x + y; } } // 테스트 메인 클래스 public class Java8Test { public static void main(String args[]) { IntBinaryOperator operator; // Class:staticMethod, 람다식 operator = (x, y) -> WedulObject.staticMethod(x, y); System.out.println( " class::staticMethod (람다) : " + operator.applyAsInt(1, 2)); // Class:staticMethod, 메서드 레퍼런스 operator = WedulObject::staticMethod; System.out.println( " class::staticMethod (메서드 레퍼런스) " + operator.applyAsInt(1, 2)); // Class:instanceMethod WedulObject object = new WedulObject(); operator = (x, y) -> object.instanceMethod(x, y); System.out.println( " class::instanceMethod (람다) " + operator.applyAsInt(1, 3) ); // Class::instanceMethod operator = object::instanceMethod; System.out.println( " class::instanceMethod (메서드 레퍼런스) " + operator.applyAsInt(1, 3)); } } | cs |
매개 변수의 메소드 참조
- 메소드는 람다식 외부의 클래식 멤버일 수도 있고, 람다식에서 제공되는 매개변수의 멤버일 수도 있다.
- 위에서 설명한 3번째 구조인 Class::instanceMethod방식에서는 첫 번째 파라미터가 해당 메서드의 대상이 될 수 있다.
Ex) (a, b) -> { a.instanceMethod(b);}
이것을 메소드 참조로 표현할 경우에는 a의 클래스 이름을 기술하고 :: 붙이고 메소드 이름을 적어주면 된다.
추가 예제
식을 하단에 소개할 Method Reference를 사용하면 더 간단하게 할 수 있다. Java8에서 새로 도입될 ::연산자를 사용하면 다음과 같이 정적 메소드나 인스턴스 메소드를 참조 할 수 있다. ::을 사용하여 지정하면 함수언어에서 에타 확장이라 알려진 과정에 의해 컴파일러가 해당 메소드를 자동으로 "확장"해서Consumer 함수형 인터페이스에 있는 유일한 추상 메소드와 같은 시그니처를 만들어 주고, 그 결과를 Consumer 인스턴스로 사용될 수 있게 된다.
※ 에타 확장
에타 확장이란 어떤 함수 f가 있을 때, (x) -> f(x)나 f가 같다는 뜻. 즉 매개변수를 붙이면 에타확장. 매개변수를 때어내면 에타 축소라고 한다.
Ex) 에타 축소 : numbers.forEach(System.out::println);
Ex) 에타 확장 : numbers.forEach((x) -> System.out::println(x))
1 2 3 4 5 6 7 | // 기존의 반복문 사용 방식 for (int i = 0; i < 10; i++) { System.out.println(i); } // 메소드 레퍼런스의 에타 축소 개념을 사용한 코드 IntStream.range(0, 10).forEach(System.out::println); | cs |
'JAVA > Java 8' 카테고리의 다른 글
Java8 인터페이스의 정적 메소드 (0) | 2018.05.30 |
---|---|
Java8 인터페이스 default Method (디폴트 메소드) (0) | 2018.05.30 |
Java8 변수 유효 범위 (0) | 2018.05.30 |
Java8 생성자 레퍼런스 (0) | 2018.05.30 |
Java8 기초 설명 (0) | 2018.05.30 |