메서드를 참조해서 매개변수 리턴 타입을 알아내어 람다식의 불필요한 매개변수를 제거하는 방법
import java.util.stream.Collectors;
public class Main {
public static class Student {
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static void main(String[] args) {
List<Student> students = List.of(
new Student("pobi"),
new Student("jun"),
new Student("woni"));
String names;
//일반 람다식
names = students.stream().map(student -> student.getName()).collect(Collectors.joining(","));
//메서드 참조 표현식
names = students.stream().map(Student::getName).collect(Collectors.joining(","));
}
}
람다식이 단 하나의 메소드만 호출할 경우에 해당 람다식에서 불필요한 매개변수를 없앨 수 있다. 이때 형식은 '클래스명::메서드명
'의 형태로 써주면 간략한 메서드 참조로 변경할 수 있다.
Map 인터페이스의 다음과 같은 merge 메서드에서도 유용하게 사용 가능하다.
default V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction)
해당 메서드는 key 값을 받아
key가 map 안에 없다면 value 값을 (key, value) 쌍으로 저장하고
key가 map 안에 있다면 value 값을 BiFunction#apply 메서드에 적용해, (key, BiFunction#apply 메서드의 결과) 쌍으로 저장한다.
BiFunction 인터페이스는 하나의 추상 메서드만 가지고 있는 함수형 인터페이스이기 때문에 익명 클래스나 람다를 이용해 구현할 수 있다.
// BiFunction 익명 클래스
map.merge(key, 1, new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer oldValue, Integer value) {
return oldValue + value;
}
});
// BiFunction 람다
map.merge(key, 1, (oldValue, value) -> oldValue + value);
이때 BiFunction은 단순히 oldValue + value
의 결과 값을 리턴해주기 때문에, 이와 동일한 기능을 하는 Integer#sum
메서드의 참조를 전달해 구현 가능하다. 위의 람다식처럼 작성하면 IDE에서 메서드 참조를 권고하는데, 이를 적용시키면 다음과 같다.
// BiFunction 메서드 참조
map.merge(key, 1, Integer::sum);
Integer::sum
이 BiFunction에 들어갈 수 있는 이유:
해당 식이 BinaryOperator로 변환되기 때문이며, BinaryOperator는 BiFunction을 상속받았기 때문이다.
메서드 참조를 사용하면 제거할 수 있는 코드양이 늘어나 더욱 간결하게 작성할 수 있지만, 람다의 매개변수 이름 자체가 프로그래머들이 코드를 이해하는데 좋은 가이드가 될 수 있어서 길이가 길더라도 코드 읽기가 좋아 유지보스에 용이하다.
예를 들어, 다음과 같은 코드가 있다고 치자.
//메서드 참조
service.execute(GoshThisClassNameIsHumongous::action);
//람다 사용
service.execute(() -> action());
메서드 참조를 사용했지만, 코드가 간결하지도 명확하지도 않다. 이럴 때는 람다(x -> x)를 직접 사용하는 것이 코드도 짧고 명확해진다.
메서드 참조에는 4가지 유형이 존재한다.
Integer::parseInt
처럼 정적 메서드를 가리키는 메서드 참조
수신 객체를 특정하지 않고 적용하는 시점에 수신 객체를 알려준다. 즉, 이미 존재하는 외부 객체를 호출하는 상황을 일컫는다. 글의 맨 앞의 예시에서 사용한 것처럼 스트림 파이프라인에서의 매핑과 필터 함수에 쓰인다.
사용 방법: 클래스명::인스턴스 메서드
ex) student -> student.getName()
-> Student::getName
정적 메서드 참조와 비슷하게 수신 객체를 특정한다. 람다의 파라미터 중 하나로 제공되는 객체의 메서드를 참조하는 것.
사용 방법: 인스턴스::메서드명
ex) (String str) -> str.length()
-> str::length
클래스 생성자를 가리키는 메서드 참조
Function<String, Student> function = Student::new;
Student student = funtion.apply("sun");
예외) 람다로는 제네릭 함수타입 구현을 표현할 수 없다. 메서드 참조로만 가능하다.
메서드 참조는 람다의 간단명료한 대안이다. 메서드 참조를 사용해 더 간결하고 명확한 의미를 나타낼 수 있다면 메서드 참조를 쓰고, 만약 그렇지 않다면 람다를 사용한다.