method reference
- 기존에 이미 선언되어 있는 메서드를 지정하고 싶을 때
- :: 오퍼레이터 사용
- ClassName::staticMethodName : 객체의 static method를 지정할 때
- ClassName::instanceMethodName : 객체의 instance method를 지정할 때
- ClassName::new : 객체의 constructor를 지정할 때
- objectName::instanceMethodName : 선언된 객체의 instance method를 지정할 때
ClassName::staticMethodName
Function<String, Integer> strToInt = Integer::parseInt;
int five = strToInt.apply("5");
objectName::instanceMethodName
String string = "hello";
Predicate<String> equalsToHello = str::eqauls;
booean isHelloEquals = equalsHello.test("world");
사용 예시
class StringUtils {
public static int countUpperCase(String str) {
int count = 0;
for (char c : str.toCharArray()) {
if (Character.isUpperCase(c)) {
count++;
}
}
return count;
}
public static void printString(String str) {
System.out.println(str);
}
}
public class MethodReferenceExample {
public static void main(String[] args) {
// static 메서드 레퍼런스
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(StringUtils::printString);
// 인스턴스 메서드 레퍼런스
String str = "HelloWorld";
int upperCaseCount = StringUtils.countUpperCase(str);
System.out.println("Number of uppercase letters: " + upperCaseCount);
}
}
생성자를 메서드 레퍼런스 방식으로 구현하는 코드를 구현해보자.
public class PersonMethodRef {
public static void main(String[] args) {
BiFunction<String, Integer, Person> personGenerator = Person::new;
Person kim = personGenerator.apply("kim", 3);
System.out.println("kim = " + kim);
}
}
보다 OOP 스러운 예시 코드를 작성해보자.
다음과 같은 input이 있고 이를 바탕으로 카 리스트를 만든다고 할 때, 종류별로 sorting을 하는 상황을 가정해보자.
String[][] inputs = new String[][] {
{"sedan", "Sonata", "Hyundai"},
{"van", "Sienna", "Toyota"},
{"sedan", "Model S", "Tesla"},
{"suv", "Sorento", "Kia"}
};
직관적으로 if else 분기문을 써서 sedan 이면 sedan 객체를 생성, van 이면 van 객체를 생성하는 식으로 코드를 작성할 수 있을 것이다.
하지만 이를 functional interface를 써서 method reference 방식으로 구현해보면 어떨까.
먼저 다음과 같은 map을 만든다.
Map<String, BiFunction<String, String, Car>> carTypeToConstructorMap = new HashMap<>();
이 map에 컨스트럭터를 던져준다.
carTypeToConstructorMap.put("sedan", Sedan::new);
carTypeToConstructorMap.put("van", Sedan::new);
carTypeToConstructorMap.put("suv", Sedan::new);
각각의 클래스는 다음과 같이 두 개의 인자를 받기 때문에 BiFunction Functional Interface의 메서드 레퍼런스 방식으로 구현이 가능하다.
public class Sedan extends Car{
public Sedan(String name, String brand) {
super(name, brand);
}
@Override
public void drive() {
System.out.println("Sedan.drive");
}
}
public class Suv extends Car{
public Suv(String name, String brand) {
super(name, brand);
}
@Override
public void drive() {
System.out.println("Suv.drive");
}
}
public class Van extends Car{
public Van(String name, String brand) {
super(name, brand);
}
@Override
public void drive() {
System.out.println("Van.drive");
}
}
결론적으로 분기문을 사용하지 않고 다음과 같이 작성할 수 있다.
public static void main(String[] args) {
Map<String, BiFunction<String, String, Car>> carTypeToConstructorMap = new HashMap<>();
carTypeToConstructorMap.put("sedan", Sedan::new);
carTypeToConstructorMap.put("van", Sedan::new);
carTypeToConstructorMap.put("suv", Sedan::new);
String[][] inputs = new String[][] {
{"sedan", "Sonata", "Hyundai"},
{"van", "Sienna", "Toyota"},
{"sedan", "Model S", "Tesla"},
{"suv", "Sorento", "Kia"}
};
List<Car> cars = Arrays.stream(inputs)
.map(input -> {
String carType = input[0];
String name = input[1];
String brand = input[2];
return carTypeToConstructorMap.get(carType).apply(name, brand);
})
.toList();
}
만약 Functional interface를 직접 구현한다면 get 부분부터 작동되는 메서드들을 아예 인터페이스 안으로 숨겨 보다 깔끔하게 만들 수도 있을 것이다.
'Programming > Java, Spring' 카테고리의 다른 글
자바 멀티 스레드 프로그래밍 (0) | 2023.06.12 |
---|---|
Java의 Arrays.sort()를 사용할 때 원시형 타입이 아닌 참조형 타입으로 전달하면 성능 향상이 가능하다 (Dual-Pivot Quicksort, Tim Sort) (0) | 2023.06.08 |
자바의 여러가지 functional interface + comparator (0) | 2023.06.03 |
자바의 Functional Interface와 andThen 메서드 이해하기 (0) | 2023.06.02 |
자바와 함수형 프로그래밍 (0) | 2023.06.02 |