메서드 참조는 말 그대로 메서드를 참조해 매개 변수의 정보와 리턴 타입을 확인하며 람다식에 불필요한 매개 변수를 제거하는 목적입니다. 람다식은 메서드 단순 호출하는 경우가 많습니다. 두 개 값을 계산하는 람다식이 있다면 메서드 참조를 이용하면 깔끔하게 처리할 수 있습니다.
(left, right) -> Math.max(left, right);
Math :: max; // 메서드 참조
IntBinaryOperator operator = Math :: max; // 메서드 참조
메서드 참조는 정적 또는 인스턴스 메서드를 참조할 수 있으며 생성자 참조도 가능합니다.
정적 메서드와 인스턴스 메서드 참조
정적(static) 메서드를 참조할 경우 클래스 이름 뒤에 :: 기호를 붙여 정적 메서드 이름을 기술하면 됩니다.
클래스 :: 메서드
인스턴스 메서드일 경우 먼저 객체를 생성하고 참조 변수 뒤에 :: 기호를 붙여 인스턴스 메서드 이름을 기술하면 됩니다.
참조변수 :: 메서드
다음 예제는 Calculator의 정적 및 인스턴스 메서드를 참조합니다. 람다식이 메서드 참조로 대체되는 것을 기억하면 됩니다.
public class Calculator {
public static int staticMethod(int x, int y) {
return x + y;
}
public int instanceMethod(int x, int y) {
return x + y;
}
}
public class MethodReferencesExample {
public static void main(String[] args) {
IntBinaryOperator operator;
// 정적 메서드 참조
operator = (x, y) -> Calculator.staticMethod(x, y);
sout("결과 1 = " + operator.applyAsInt(1, 2));
operator = Calculator :: staticMethod;
sout("결과 2 = " + operator.applyAsInt(3, 4));
Calculator obj = new Calculator();
operator = (x, y) -> obj.instanceMethod(x, y);
sout("결과 3 = " + operator.applyAsInt(5, 6));
operator = obj :: instanceMethod;
sout("결과 4 = " + operator.applyAsInt(7, 8));
}
}
메서드는 람다식 외부의 클래스 멤버일 수도 있고, 람다식에서 제공되는 매개 변수의 멤버일 수도 있습니다. 람다식 외부 클래스 멤버인 메서드를 호출했습니다. 람다식에서 제공되는 a 변수의 메서드를 호출해서 b 매개 변수를 매개값으로 사용하는 경우도 있습니다.
(a, b) -> { a.instanceMethod(b); }
이것을 메서드 참조로 표현하면 a 클래스 이름 뒤에 :: 기호를 붙여 메서드 이름을 기술하면 됩니다. 작성 방법은 정적 메서드 참조와 동일하지만 a의 인스턴스 메서드가 참조되므로 전혀 다른 코드가 실행된다.
클래스 :: instanceMethod
다음 예제는 두 문자열이 대소문자 상관없이 동일한 알바펫으로 구성되어 있는지 비교합니다. 비교를 위해 사용된 메서드는 String 인스턴스 메서드인 compareToIgnoreCase()입니다. a.compareToIgnoreCase(b)로 호출될 때 사전 순으로 a가 b보다 먼저 오면 음수를, 동일하면 0을, 나중에 오면 양수를 리턴합니다. 사용된 함수적 인터페이스는 두 String 매개값을 받고 int 값을 리턴하는 ToIntBiFunction<String, String> 입니다.
public class ArgumentMethodReference {
public static void main(String[] args) {
ToIntBiFunction<String, String> function;
function = (a, b) -> a.compareToIgnoreCase(b);
print(function.applyAsInt("java8", "JAVA8"));
function = String :: compareToIgnoreCase;
print(finction.applyAsInt("java8", "JAVA8"));
}
public static void print(int order) {
if(order < 0) { sout("사전순으로 먼저 옵니다."); }
else if(order == 0) { sout("동일한 문자열입니다."); }
else { sout("사전순으로 나중에 옵니다"); }
}
}
메서드 참조는 생성자 참조도 포함합니다. 생성자를 참조한다는 것은 객체 생성을 의미합니다. 단순히 메서드 호출로 구성된 람다식을 메서드 참조로 대치할 수 있듯이 단순히 객체를 생성하고 리턴하도록 구성된 람다식은 생성자 참조로 대치할 수 있습니다.
다음 코드는 단순히 객체 생성 후 리턴만 합니다.
(a, b) -> { return new 클래스(a, b); }
이 경우 생성자 참조로 표현하면 클래스 이름 뒤에 :: 기호를 붙이고 new 연산자를 기술하면 됩니다. 생성자가 오버로딩되어 여러 개가 있을 경우 컴파일러는 함수적 인터페이스의 추상메서드와 동일한 매개변수 타입과 개수를 갖고 있는 생성자를 찾아 실행합니다. 만약 해당 생성자가 존재하지 않으면 컴파일 오류가 발생합니다.
클래스 :: new
public class ConstructorRef {
public static void main(String[] args) {
Function<String, Member> function1 = Member :: new;
Member member1 = function1.apply("angel");
BiFunction<String, String, Member> function2 = Member :: new;
Member member2 = function2.apply("신천사", "angel");
}
}
public class Member {
private String name;
private String id;
public Member() {
sout("Member() 실행");
}
public Member(String id) {
sout("Member(String id) 실행");
this.id = id;
}
public Member(String name, String id) {
sout("Member(String name, String id)");
this.name = name;
this.id = id;
}
}
List - 컬렉션 프레임워크 (1) | 2024.02.07 |
---|---|
함수형 인터페이스 (Operator, Predicate), andThen(), compose() (1) | 2024.02.06 |
JPA 복합키 식별관계 (필수적, 선택적, @IdClass, @EmbeddedID) (0) | 2024.02.04 |
함수 인터페이스 (Consumer, Supplier, Function) (1) | 2024.02.03 |
람다식 사용법 (0) | 2024.02.02 |
댓글 영역