1. 자바 함수형 인터페이스
- Function : 인수를 받고 결과를 반환 한다.
- Consumer : 인수만 받고 결과는 반환 하지 않는다.
- Supplier : 인수를 받지 않고 결과만 반환 한다.
- Predicate : 인수를 받아서 표현식에 대해 테스트하고 boolean 값을 결과로 반환 한다.
Function
- 하나의 입력과 출력을 가진 전통적인 함수
- 단일 추상 메서드는 apply 로 T 타입의 인수를 받아 R 타입의 결과를 생성한다.
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
문자열을 정수로 변환하거나 객체의 속성을 추출
Function<String, Integer> strLength = str -> str != null ? str.length() : 0;
Integer length = strLength.apply("fucking hard");
System.out.println(length); // 12
하나의 데이터 형식을 다른 데이터 형식으로 매핑
Function<String, String> toUpperCase = String::toUpperCase;
List<String> names = List.of("park", "seong", "hyeon");
List<String> upperNames =
names.stream()
.map(toUpperCase)
.collect(Collectors.toList());
System.out.println(names); // park seong hyeon
System.out.println(upperNames); // PARL SEONG HYEON
여러 함수를 연속적으로 실행하여 데이터 변환 작업 수행 가능
Function<String, String> toUpperCase2 = String::toUpperCase;
Function<String, String> removeWhiteSpace = String::trim;
Function<String, String> pipeline = toUpperCase2.andThen(removeWhiteSpace);
String result = pipeline.apply(" hello ");
System.out.println(result); // 공백 없는 hello
Consumer
- 입력 파라미터를 소비하지만 아무것도 반환하지 않는다.
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
public static void main(String[] args){
// 일반적인 void 메서드
void println(String str){
System.out.println(str);
}
println("Hello");
// Consumner 사용
Consumer<String> println = str -> System.out.println(str);
println.accept("Hello");
}
Supplier
- Consumer의 반대이며, 어떠한 입력 파라미터도 받지 않지만 단일값을 반환한다.
- Supplier는 지연실행에 사용된다.
@FunctionalInterface
public interface Supplier<T> {
T get();
}
Supplier<Integer> randomIntegerSupplier = () -> (int) (Math.random() * 100);
Integer randomNumber = randomIntegerSupplier.get();
System.out.println(randomNumber); // 랜덤 숫자 출력
지연 실행 Deferred execution
- 코드 블록이나 함수를 나중에 필요한 시점에 실행되도록 하는 프로그래밍 패턴
- 비용이 많이 드는 작업을 Supplier로 래핑하고 필요할 때만 get을 호출
- 예를 들어, DB 조회시 연결이나 쿼리 실행은 비용이 크고 시간이 걸리는 작업이다. 프로그램이 시작될 때보다 실제로 데이터가 필요한 시점에 DB 연결을 수행하는 것이 효율적이다.
Predicate
- 단일 인수를 받아 참인지 거짓인지 평가한다.
- 보통 조건문에서 사용되나 컬렉션의 요소를 필터링할 때 유용하다.
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 짝수 필터링 정의
Predicate<Integer> isEven = num -> num % 2 == 0;
// 필터링 결과 출력
numbers.stream()
.filter(isEven)
.forEach(num -> System.out.println(num + " "));
}
2. 함수 합성
글루 메서드 (glu method) : 여러 가지 요소를 서로 연결하거나 결합하는 역할을 하는 메서드
- 함수 함성으로 작은 함수들을 결합하여 복잡한 작업을 처리할 수 있다.
- 자바에서는 글루 메서드를 사용하는 데 글루 메서드는 기본적으로 함수형 인터페이스 자체에 직접 구현된다.
- 결합된 기능을 가진 새로운 인터페이스를 반환함으로써 두 함수형 인터페이스 사이의 연결고리를 만든다.
default <V> Function<V, R> compose(Function<? super V, ? extends T> before)
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after)
// compose
Function<Integer, Integer> func = num -> num + num;
Function<Integer, Integer> composeFunc = num -> num * num;
// composeFunc이 먼저 실행된 후, func 실행
Integer apply = func.compose(composeFunc).apply(5);
System.out.println(apply); // 5 * 5 -> 25 + 25 -> 50
// andThen
Function<Integer, Integer> func = num -> num + num;
Function<Integer, Integer> andThenFunc = num -> num * num;
// func이 먼저 실행된 후, andThenFunc 실행
Integer apply = func.andThen(andThenFunc).apply(5);
System.out.println(apply); // 5 + 5 -> 10 * 10 -> 100
3. 함수 지원 확장
- 기존 타입을 더 함수적으로 만들기 위해 인터페이스에 기본 메서드 추가
- 공통 함수형 작업을 제공하기 위해 정적 헬퍼를 생성한다.
기본 메서드
- 인터페이스에 새로운 기능을 추가할 때마다 모든 구현에 새로운 메서드를 구현해야 한다.
- 프로젝트 크기가 큰 경우, 라이브러리 코드의 경우 모든 구현을 업데이트 하는 것은 쉽지 않다.
- 이러한 상황에 기본 메서드를 사용한다.
// Collection의 구현 클래스에 stream 기능을 사용하여
// 함수적으로 코드를 작성할 수 있다.
public interface Collection<E> extends Iterable<E> {
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
}
public class StreamEx {
public static void main(String[] args) {
List<Member> members = createMember();
members.stream()
.filter(member -> member.nameLength())
.map(member -> member.getName())
.map(name -> name.toUpperCase())
.forEach(name -> System.out.println(name));
// 출력
// STEVE
// KENNY
// MARIA
}
static List<Member> createMember() {
return Arrays.asList(
new Member("john"),
new Member("steve"),
new Member("dave"),
new Member("kenny"),
new Member("maria")
);
}
static class Member {
String name;
public Member(String name) {
this.name = name;
}
public String getName() {
return name;
}
boolean nameLength() {
return this.name.length() >= 5;
}
}
}
정적 헬퍼
- 정적 멤버로 구현된 유틸리티 메서드나 상수를 가지고 있다.
- 여러 곳에서 공통적으로 사용되는 기능이나 상수를 제공한다.
- 함수형 인터페이스와 정적 헬퍼 메서드를 조합하여 다양성을 확장할 수 있다.
public class PredicateUtils {
// null이 아닌지 확인하는 Predicate
public static <T> Predicate<T> isNotNull() {
return t -> t != null;
}
// 주어진 문자열이 비어있지 않은지 확인하는 Predicate
public static Predicate<String> isNotEmptyString() {
return s -> s != null && !s.isEmpty();
}
}
'함수형 프로그래밍' 카테고리의 다른 글
[ch.06] 스트림을 이용한 데이터 처리 (0) | 2024.06.06 |
---|---|
[ch.05] 레코드 (0) | 2024.06.01 |
[ch.04] 가변성 & 불변성 (0) | 2024.05.23 |
[ch.02] 함수형 자바 (0) | 2024.05.09 |
[ch.01] 함수형 프로그래밍 소개 (1) | 2024.05.02 |