자바 8 라이브러리 설계자들은 java.util.function 패키지로 여러가지 새로운 함수형 인터페이스를 제공한다.
Predicate
java.util.function.Predicate<T> 인터페이스는 test라는 추상 메서드를 정의하며 test는 제네릭 형식 T의 객체를 인수로 받아 boolean을 반환한다. T 형식의 객체를 사용하는 boolean 표현식이 필요한 상황에서 Predicate 인터페이스를 사용할 수 있다.
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
public <T> List<T> filter(List<T> list, Predicate<T> P) {
List<T> results = new ArrayList();
for (T t:list) {
if (p.test(t)) {
results.add(t);
}
}
return results;
}
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);
Predicate 인터페이스의 자바독 명세(javadoc specification)을 보면 and나 or 같은 메서드도 있음을 수 있다.
Consumer
java.util.function.Consumer<T> 인터페이스는 제네릭 형식 T 객체를 받아서 void를 반환하는 accept라는 추상 메서드를 정의한다. T 형식의 객체를 인수로 받아서 어떤 동작을 수행하고 싶을 때 Consumer 인터페이스를 사용할 수 있다.
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
public <T> void forEach(List<T> list, Consumer<T> c) {
for (T t:list) {
c.accept(t);
}
}
forEach(Arrays.asList(1,2,3,4,5), (Integer i) -> System.out.println(i));
Function
java.util.function.Function<T, R> 인터페이스는 제네릭 형식 T를 인수로 받아서 제네릭 형식 R 객체를 반환하는 추상 메서드 apply를 정의한다. 입력을 출력으로 매핑하는 람다를 정의할 때 Function 인터페이스를 활용할 수 있다.
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
public <T, R> List<R> map(List<T> list, Function<T, R> f) {
List<R> result = new ArrayList();
for (T t:list) {
result.add(f.apply(t));
}
return result;
}
List<Integer> I = map(
Arrays.asList("lambdas", "in", "action"),
(String s) -> s.length()
);
기본형 특화
자바의 모든 형식은 참조형(Byte, Integer, Object, List, .. ) 아니면 기본형(int, double, byte, char, ... )에 해당한다.
하지만 제네릭 파라미터에는 제네릭 내부 구현 때문에 어쩔 수 없이 참조형만 사용할 수 있다.
자바에서는 기본형을 참조형으로 변환하는 기능을 제공한다.
이 기능을 박싱(boxing)이라고 하고, 참조형을 기본형으로 변환하는 반대 동작을 언박싱(unboxing)이라고 한다.
프로그래머가 편리하게 코드를 구현할 수 있도록 박싱과 언박싱이 자동으로 이루어지는 오토박싱(autoboxing)이라는 기능도 제공한다.
하지만 이렇게 변환하는 과정에서 메모리를 탐색해 비용이 소모되므로 자바 8에서는 기본형을 입출력으로 사용하는 상황에서 오토박싱 동작을 피할 수 있도록 특별한 버전의 함수형 인터페이스를 제공한다.
Predicate<Integer> oddNumbers = (Integer i) -> i % 2 != 0;
oddNumbers.test(1000);
위에 코드는 박싱이 자동으로 동작해 오토박싱된 코드이다.
오토박싱 동작을 피할 수 있도록 특별한 함수형 인터페이스를 사용하면 아래처럼 사용할 수 있다.
IntPredicate evenNumbers = (int i) -> i % 2 == 0;
evenNumbers.test(1000);
일반적으로 특정 형식을 입력으로 받는 함수형 인터페이스의 이름 앞에는 DoublePredicate, IntConsumer, LongBinaryOperator, IntFuntion처럼 형식명이 붙는다.
Function 인터페이스는 ToIntFunction<T>, IntToDoubleFunction 등의 다양한 출령 형식 파라미터를 제공한다.
'Java' 카테고리의 다른 글
[모던 자바] 람다 표현식을 조합할 수 있는 유용한 메서드 (0) | 2022.03.02 |
---|---|
[모던 자바] Java 8 메서드 참조와 생성자 참조란? (0) | 2022.03.02 |
[모던 자바] 람다란 무엇인가? (0) | 2022.02.28 |
[모던 자바] 동작 파라미터화란 무엇인가? (2) | 2022.02.27 |
JitPack을 이용한 Java 패키지 배포 (0) | 2021.12.10 |