함수형 인터페이스에 앞서 함수형 프로그래밍에 대해 이해해야 한다.
함수형 프로그래밍이란 '작업을 어떻게 수행할 것인지'에 집중하는 것이 아니라 '무엇을 할 것인지'에 집중하는 것이다.
구체적인 작업은 라이브러리가 알아서 처리하고, 사용자는 라이브러리가 제공하는 인터페이스를 구현하는 것으로 원하는 작업을 수행한다.
함수형 인터페이스란, 하나의 추상 메서드만 가지는 인터페이스이다.
함수형 인터페이스는 람다로 구현할 수 있다.
대신 여러 개의 메서드를 구현할 경우 함수형 인터페이스가 아니므로 람다로 구현할 수 없다.
// 함수형 인터페이스 선언
@FunctionalInterface
interface FunctionalInterfaceExample {
int add(int a, int b);
}
// 함수형 인터페이스 구현. 람다식으로 구현이 가능하다.
FunctionalInterfaceExample example = (a, b) -> a + b;
int result = example.add(3,4);
System.out.println(result);
// 결과 출력
7
자바에는 함수형 프로그램을 지원하는 라이브러리를 제공하고 있다.
공식문서: java.util.function
그중 자주 사용되는, 혹은 사용해 볼 만한 함수형 인터페이스를 정리해 보고자 한다.
- Function <T, R>
- Consumer
- Supplier
- Predicate
- UnaryOperator
- BinaryOperator
1. Function <T, R>
T 타입을 인자로 받아 R 타입을 반환하는 함수형 인터페이스이다. 메서드: R apply(T t);
(T와 R의 타입은 같을 수도 있고, 다를 수도 있다.)
아래 코드처럼, String 타입을 받아 Integer타입을 반환하고 있다.
Function<String, Integer> function = value -> Integer.parseInt(value) + 10;
Integer result = function.apply("10");
System.out.println(result);
System.out.println(result.getClass());
// 출력
20
class java.lang.Integer
아래 코드는 사다리 미션을 구현하면서 사용했던 코드의 일부이다.
사용자로부터 올바른 입력을 받을 때까지 재입력을 받아오기 위해 사용한 코드이다.
depth 1이라는 프로그래밍 요구사항을 만족시키기 위해 재귀를 사용했다.(그게 아니라면 while문을 사용하는 게 나을 것 같다.)
Function <T, R> 타입을 메서드 파라미터로 받고 있다. T(Participant)를 받아 R(Ladder)를 리턴한다.
// T 인자를 받아서 apply()의 메서드로 넣어주고, R 타입을 반환한다.
private <T, R> R repeatUntilSuccess(Function<T, R> function, T input) {
try {
return function.apply(input);
} catch (IllegalArgumentException e) {
outputView.printErrorMessage(e.getMessage());
return repeatUntilSuccess(function, input);
}
}
// Function<T, R> function 부분에 수행하려는 로직을 넣어줄 수 있다.
Ladder ladder = repeatUntilSuccess(this::prepareLadder, participants);
private Ladder prepareLadder(Participants participants) {
Height ladderHeight = new Height(inputView.requestLadderHeight());
int numberOfParticipants = participants.getParticipantsSize();
return new Ladder(ladderHeight, numberOfParticipants);
}
2. Consumer
T 타입 인자를 받아서 소비하는 함수형 인터페이스이다. 메서드: void accept(T t);
입력값을 받아 출력을 반환하지 않고, 입력값을 소비할 때 사용한다.
아래와 같이 입력값을 받아 리턴값 없이 소비할 때 사용할 수 있다.
Consumer<String> consumer = value -> System.out.println(Integer.parseInt(value) + 10);
consumer.accept("5");
// 출력
15
3. Supplier
인자를 받지 않고 T타입 값을 반환하는 함수형 인터페이스이다. 메서드: T get();
입력값 없이 출력값만 있는 경우 사용할 수 있다.
Supplier<Integer> supplier = () -> 10;
Integer result = supplier.get();
System.out.println(result);
// 출력
10
Function<T,R>에서와 마찬가지로 사다리 미션을 구현하면서 사용했던 Supplier의 코드를 가져왔다.
사용자로부터 사다리 게임에 참여할 참여자의 목록을 가져오는 부분이다.
// 파라미터가 없는 메서드를 올바른 입력이 들어올때까지 반복하는 메서드이다.
private <T> T repeatUntilSuccess(Supplier<T> supplier) {
try {
return supplier.get();
} catch (IllegalArgumentException e) {
outputView.printErrorMessage(e.getMessage());
return repeatUntilSuccess(supplier);
}
}
// 파라미터가 없는 prepareParticipants()를 사용하고있다.
Participants participants = repeatUntilSuccess(this::prepareParticipants);
private Participants prepareParticipants() {
List<String> names = inputView.requestParticipantsName();
return new Participants(names);
}
4. Predicate
T 타입의 인자를 받아서 boolean 값을 반환하는 함수형 인터페이스이다. 메서드: boolean test(T t);
Predicate<String> predicate = value -> value.equals("a");
boolean result = predicate.test("b");
System.out.println(result);
// 출력
false
5. UnaryOperator
Function<T, R>의 특수한 형태로 T타입의 값을 받아 T타입을 리턴하는 함수형 인터페이스이다.
(Function<T, T> 를 상속한 인터페이스로, Function<T, T>의 메서드도 구현할 수 있다.)
메서드: static <T> UnaryOperator<T> identity();
UnaryOperator<Integer> unaryOperator = i -> i * 10;
int result = unaryOperator.apply(3);
System.out.println(result);
// 출력
30
6. BinaryOperator
BiFunction<T, U, R>의 특수한 형태로 T 형태의 입력값 두개를 받아 T타입을 리턴하는 함수형 인터페이스이다.
(BiFunction<T, T, T>를 상속한 인터페이스로, BiFunction<T, T, T>의 메서드도 구현할 수 있다.)
메서드: static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator)
메서드: static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator)
BinaryOperator<Integer> binaryOperator = (a, b) -> a + 2 * b;
int result = binaryOperator.apply(3,5);
System.out.println(result);
// 출력
13
여기까지 여러가지 함수형 인터페이스와 사용 예시에 대해 간단히 정리해보았다.
파라미터 유무, 파라미터와 리턴 형태, 파라미터 갯수 등을 고려해서 적절한 인터페이스를 사용하면 된다.
'우아한테크코스 > 레벨 1 - Java' 카테고리의 다른 글
[GitHub] 코드리뷰가 익숙하지 않은 분들을 위한 GitHub에서 코드리뷰 하는법 (0) | 2024.03.30 |
---|---|
[JAVA] 상태 패턴(State Pattern) with BlackJack (21) | 2024.03.19 |
[JAVA] 캐싱 활용하기 (0) | 2024.03.11 |
[JAVA] VO(Value Object) vs DTO(Data Transfer Object) with record (13) | 2024.03.05 |
[JAVA] static 키워드와 static factory method (4) | 2024.02.29 |