Java8 functional interface - function, supplier, consumer
Java8에서 나와 주로 람다식으로 자주 사용되고 있으며, 함수형 패러다임을 달성할 수 있도록 하는데 도움, 해당 interface들은 java.util.function 패키지에 모두 포함
1개의 파라미터를 받아서 1개의 결과를 반환하는 functional interface, Function<T, R>로 사용할 수 있으며 T는 받는 파라미터, R은 반환 파리미터
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
// EX
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
대표적인 EX
Stream에서 map, map은 Stream의 형태를 Stream로 변경하는 메서드. 파라미터로 Function interface를 받음
private Function<Integer, String> stringMap = integer -> String.valueOf(integer);
@Test
public void FunctionTest() {
Stream<Integer> integerStream = Stream.of(1, 2);
List<String> collect = integerStream.map(stringMap).collect(Collectors.toList());
System.out.println("collect = " + collect);
}
Supplier functional interface는 값을 생성하기 위해서 사용, 함수형이기 때문에 lazy하게 처리할 수 있음. Supplier의 형태를 가지며 T는 반환 타입
@FunctionalInterface
public interface Supplier<T> {
T get();
}
// EX
public static<T> Stream<T> generate(Supplier<T> s) {
Objects.requireNonNull(s);
return StreamSupport.stream(
new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
}
대표적인 EX
Stream의 generate, generate는 Stream 객체를 만드는 메서드로 Supplier를 파라미터로 받으며, Supplier에서 반환된 T값을 사용. Supplier는 선언되었을 떄 바로 사용되는 것이 아니라 generate에서 사용될 때 값을 반환하는 형식임으로 lazy를 따른다고 볼 수 있음
@Test
public void SupplierTest() {
int i = 5;
Supplier<Integer> integerSupplier = () -> i * i;
Optional<Integer> first = Stream.generate(integerSupplier).findFirst();
System.out.println("first.get() = " + first.get());
}
Supplier과는 반대로 Consumer interface는 단일 파라미터를 받아들이고 아무런 리턴값이 없는 functional interface, Consumer로 사용하고 T는 받는 단일 객체의 타입
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
}
//EX
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
대표적인 EX
foreach, for를 대신해 list를 간단히 처리하고자 할 떄 사용, Consumer라는 functional interface를 구현한 로직을 for구문으로 실행
@Test
public void ConsumerTest() {
Consumer<Integer> consumer = integer -> System.out.println(integer);
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);
integers.forEach(consumer);
}
Predicate Interface는 T에 대한 조건에 대해서 true/false를 반환하는 Functional Interface, Predicate로 사용되며 T는 파라미터(해당 파라미터에 대해서 true/false를 return 할 수 있도록 작성
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
}
// EX
Stream<T> filter(Predicate<? super T> predicate);
대표적인 EX
Stream의 filter, filter는 Stream의 요소 중 통과할 요소와 제거할 요소를 걸러내는 작업을 해주는 함수형 명령어. 각 요소에 대해서 조건의 만족 유무에 따라서 제거
@Test
public void predicateTest() {
Predicate<Integer> justOne = integer -> integer == 1;
Stream<Integer> integerStream = Stream.of(1, 2);
Stream<Integer> filteredStream = integerStream.filter(justOne);
System.out.println("collect = " + filteredStream.collect(toList()));
}
UnaryOperator는 입력받은 파라미터 타입과 리턴 받는 타입이 동일한 Functional Interface, Functional Interface를 확장. Function Interface는 T → R이며, UnaryOperator는 T→T(실제 코드에서도 Function Interface를 확장해서 사용)
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
...
}
// EX
default void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
final ListIterator<E> li = this.listIterator();
while (li.hasNext()) {
li.set(operator.apply(li.next()));
}
}
대표적인 EX
ArrayList의 메서드 중 replaceAll, ArrayList의 elements들을 일괄로 특정 식을 통해서 변경하는 메서드. iterator로 해당 list를 순회하면서 UnaryOperator의 수식을 각 element에 적용하는 코드를 확인할 수 있고, 해당 코드를 사용하기 위해 UnaryOperator Interface를 구현해야 함
@Test
public void unaryOperatorTest() {
UnaryOperator<Integer> doubleOp = i -> i * 2;
List<Integer> list = Arrays.asList(1, 2);
list.replaceAll(doubleOp);
System.out.println("list = " + list);
}
BinaryOperator는 2개의 동일한 타입의 파라미터로 1개의 동일한 리턴 값을 가져오는 Functional Interface, BiFunction Interface를 확장한 interface로 (T, U) → R을 응용하여 (T, T) → T로 이용
@FunctionalInterface
public interface BiFunction<T, U, R> {
/**
* Applies this function to the given arguments.
*
* @param t the first function argument
* @param u the second function argument
* @return the function result
*/
R apply(T t, U u);
}
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
...
}
// EX
Optional<T> reduce(BinaryOperator<T> accumulator);
대표적인 EX
Stream의 reduce, Stream의 elements들이 중첩하여 결과를 만드는 메서드. 첫번쨰(first)파라미터는 이전 연산의 결과값이고 2번째(second)파라미터는 새로 들어온 값
@Test
public void binaryOperatorTest() {
BinaryOperator<Integer> operator = (first, second) -> first + second;
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
Optional<Integer> reduce = integerStream.reduce(operator);
System.out.println("reduce = " + reduce.get());
}