다양한 함수형 인터페이스
3.1 Predicate
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
}
Predicate는 동작 파라미터화의 두 번째 장에서 다루었던 인터페이스입니다. 여러 디폴트 메서드로 Predicate의 변환을 유연하게 할 수 있게 도와주며 필터링을 하는 상황에서 유용하게 쓰일 수 있습니다.
기존 코드에서는 Predicate의 조합을 사용한 적은 없는데 기본 메서드들을 활용하면 아래와 같은 코드를 작성할 수 있습니다.
예제 코드
public class FilteringBooks {
public static void main(String[] args) {
List<Book> bookshelf = Arrays.asList(new Book(510, Science),
new Book(100, Economy),
new Book(120, Science));
Predicate<Book> bookThickPredicate = (Book book) -> book.getPage() >= 500;
Predicate<Book> bookCategorySciencePredicate = (Book book) -> Science.equals(book.getCategory());
List<Book> thinBooks = filterBooks(bookshelf, bookThickPredicate.negate()); // negate 메서드 활용
List<Book> thickScienceBooks = filterBooks(bookshelf, bookThickPredicate.and(bookCategorySciencePredicate)); // and 메서드 활용
List<Book> scienceOrEconomyBooks = filterBooks(bookshelf, bookCategorySciencePredicate.or((Book book) -> Economy.equals(book.getCategory()); // or 메서드 활용
}
public static List<Book> filterBooks(List<Book> bookshelf, Predicate<Book> bookPredicate) {
List<Book> result = new ArrayList<>();
for (Book book : bookshelf) {
if (bookPredicate.test(book)) {
result.add(book);
}
}
return result;
}
}
3.2 Consumer
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
Consumer는 이름 그대로 소비자의 역할을 하며 하나의 매개변수를 받아서 로직을 수행하고 반환값을 가지지 않는 accept 메서드를 구현해야 합니다. 그리고 andThen 메서드를 이용해서 두 개의 Consumer 동작을 결합할 수도 있습니다.
예제 코드
Consumer<Book> bookPagePrinter = (Book book) -> { System.out.println(book.getPage()); };
Consumer<Book> bookTitlePrinter = (Book book) -> { System.out.println(book.getTitle()); };
Consumer<Book> bookTitleAndPagePrinter = bookTitlePrinter.andThen(bookPagePrinter);
Book book = new Book(500, "book title");
bookTitleAndPagePrinter.accept(book);
3.3 Function
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
Funtion의 경우에는 매개변수와 반환값의 타입을 모두 고려해야 해서 Consumer나 Predicate에 비해 조금 복잡한 편입니다.함수를 결합해 주는 compose 메서드와 순차적으로 함수를 실행해 주는 andThen 메서드가 정의되어 있습니다.
예제코드
Function<Book, Integer> getBookPageFunction = (Book book) -> book.getPage();
Function<String, Book> getNewTitleBookFucntion = (String title) -> new Book(500, title);
Function<Integer, Book> getNewPageBookFunction = (Integer pageCount) -> new Book(pageCount, "None");
// (String) -> Integer
Integer page = getBookPageFunction.compose(getNewTitleBookFunction).apply("Hello World");
// (Book) -> Book
Book newPageBook = getBookPageFunction.andThen(getNewPageBookFunction).apply(new Book(500, "Title"));
'독서 > 모던 자바 인 액션' 카테고리의 다른 글
데이터 컬렉션의 반복 처리 - 스트림(Stream) (1) | 2023.09.26 |
---|---|
람다 표현식을 더 간결하게 - 메서드 참조 (0) | 2023.09.25 |
간결한 코드를 만드는 법❓- 람다 표현식 (0) | 2023.09.24 |
동작 파라미터화2 (0) | 2023.09.24 |
동작 파라미터화1 (0) | 2023.09.24 |