안녕하세요. 오늘은 저번 포스팅에 이어서 functional interface를 알아보는 2번째 시간입니다. 오늘 알아볼 Functional Interface는 Predicate, Operate 2가지입니다.
Predicate
Predicate Interface는 T에대한 조건에 대해서 true / false를 반환하는 Fucntional Interface입니다. Predicate<T>
로 사용되며 T는 파라미터입니다. 해당 파라미터에 대해서 true / false를 return 할 수 있도록 작성해주면 됩니다.
Predicate interface는 아래와 같습니다.
@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);
}
Predicate가 사용되는 가장 대표적인것은 Stream의 filter입니다. Stream에서 filter는 Stream의 요소중 통과할 요소와 제거할 요소를 걸러내는 작업을 해주는 함수형 명령어입니다.
/**
* Returns a stream consisting of the elements of this stream that match
* the given predicate.
*
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
* operation</a>.
*
* @param predicate a <a href="package-summary.html#NonInterference">non-interfering</a>,
* <a href="package-summary.html#Statelessness">stateless</a>
* predicate to apply to each element to determine if it
* should be included
* @return the new stream
*/
Stream<T> filter(Predicate<? super T> predicate);
위에 설명에도 나와있듯이 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
UnaryOperator은 입력받은 파라미터 타입과 리턴 받는 타입이 동일한 Functional Interface입니다. UnaryOperator
는 Functional Interface를 확장하였습니다. Function Interface는 T -> R 이며, UnaryOperator는 T -> T 입니다. 실제 코드에서도 아래와 같이 Funtion Interface를 확장해서 사용하고 있습니다.
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
...
}
UnaryOperator는 우리가 많이 사용하는 ArrayList의 메서드중 하나에서 찾아 볼 수 있었습니다. replaceAll
이라는 메서드입니다. 해당 메서드는 ArrayList의 elements들을 일괄로 특정 식을 통해서 병경하는 메서드 입니다. 아래는 해당 메서드의 내부 구조입니다.
default void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
final ListIterator<E> li = this.listIterator();
while (li.hasNext()) {
li.set(operator.apply(li.next()));
}
}
iterator로 해당 list를 순회하면서 UnaryOperator의 수식을 각 element에 적용하는 코드를확인할 수 있었습니다. 해당 코드를 우리가 사용하기 위해서는 UnaryOperator Interface를 구현하여야하며 아래와 같이 작성할 수 있습니다. 아래의 코드의 결과는 list = [2, 4]
로 출력될 것입니다.
@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
BinaryOperator는 2개의 동일한 타입의 파라미터로 1개의 동일한 리턴 값을 가져오는 Functional Interface입니다. BinaryOperator는 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> {
...
}
BinaryOperator는 Stream의 reduce에서 찾아볼 수 있었습니다. reduce는 Stream의 elements들이 중첩하여 결과를 만드는 메서드입니다. 즉 1, 2, 3 이라는 요소가 있고 이를 reduce로 더하면 (1 + 2), (3[1 + 2의 결과] + 3)로 결국 6의 결과를 내놓습니다.
Optional<T> reduce(BinaryOperator<T> accumulator);
reduce의 는 위와같이 정의되어 있습니다. 그리고 아래와같이 사용할 수 있습니다. 첫번째(first) 파라미터는 이전 연산의 결과값입니다. 그리고 2번째(second) 파라미터는 새로 들어온 값입니다. 위에서 설명했다시피 로직은 실행되며 아래의 결과는 1 + 2 + 3 +4 + 5로 15가 나옵니다.
@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());
}
마무리
오늘은 저번시간에 이어서 2번째 functional interface에 대해서 알아보는 시간으로 Predicate와 UnaryOperator, 그리고 BinaryOperator에 대해서 알아보는 시간을 가졌습니다.
감사합니다.
참조
'language, framework, library > Java' 카테고리의 다른 글
[Java] Reactive Stream 이란? (2) | 2020.07.02 |
---|---|
[Java] 자바에서 줄바꿈, 개행의 규칙 (0) | 2020.06.26 |
[java8] java의 주요 functional interface - function, supplier, consumer (0) | 2020.05.09 |
[java] exception 처리하기 - try-catch-with-resources (1) | 2020.04.26 |
[java] exception 처리하기 - throws에 관하여 (1) | 2020.04.22 |
댓글