快速了解Consumer、Supplier、Predicate與Function
一、前言
這幾個接口都處在java.util.function包下,Consumer(消費型),Supplier(供給型)、Predicate(判斷型)與Function(轉換型),暫時不了解他們的類型沒關系。
如果對Lambda不怎麼了解的同學,可以先移步到我的另外一篇文章對Lambda的了解
二、Consumer
Consumer是一個消費型的接口,它接收一個🍉,然後對這個西瓜進行消費,連西瓜籽都不帶留下的。
先看Consumer接口的源碼,有一個未實作的抽象方法,和一個預設方法(jdk1.8之後,接口裡面可以有預設方法和靜态方法)。
@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); };
}
}
我們隻在意這個accept方法,接收一個泛型參數,不傳回任何值。ok,我們來簡單實作它
Consumer<Integer> consumer=new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
};
consumer.accept(1);
}
好了,用腳指頭想着,肯定是輸出1了。
接下來我們使用lambda表達式來對此匿名内部類進行改寫。此時該lambda的類型就是Consumer類型。
consumer=i-> System.out.println(i);
當然我們也可以使用方法引用
consumer=System.out::println;
在Stream類中,我們發現常用的forEach接口接收一個Consumer類型的參數,源碼如下
void forEach(Consumer<? super T> action);
二話不說,我們将consumer傳入forEach中,來實作周遊集合的操作。
List<Integer> list= Arrays.asList(1,2,3,4,5);
Consumer<Integer> consumer= System.out::println;
list.stream().forEach(consumer);
将中間consumer對象去掉呢,代碼會變得更加簡潔。咦,到這裡,是不是有一種似曾相識的感覺,原來是這樣演變來的。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.stream().forEach(System.out::println);
Consumer總結:
- Consumer接口是一個消費型的接口,隻要實作它的accept方法,就能作為消費者來輸出資訊。
- lambda、方法引用都可以是一個Consumer類型,是以他們可以作為forEach的參數,用來協助Stream輸出資訊。
- Consumer還有很多變種,例如IntConsumer、DoubleConsumer與LongConsumer等,歸根結底,這些變種其實隻是指定了Consumer中的泛型而已,方法上并無變化。
三、Supplier
Supplier是一個供給型的接口,我們可以無條件的從它這裡擷取東西。
@FunctionalInterface
public interface Supplier<T> {
T get();
}
我們不需要為get方法傳入任何參數,就能獲得一個結果,這不是白嫖嗎?那我想要一個随機數
Supplier<Double> supplier=()->new Random().nextDouble();
//當然也可以使用方法引用
Supplier<Double> supplier1= Math::random;
System.out.println(supplier.get());
下一步,Supplier可以哪些地方呢,畢竟是可以白嫖的,誰不喜歡呢?我們看看Supplier在Optional中的應用。
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
該方法接收Supplier類型的參數,當Optional内部的value不為空時,才會傳回Supplier中的值。例如
Optional<Double> optional=Optional.empty();
Supplier<Double> supplier=()->new Random().nextDouble();
optional.orElseGet(supplier);
這必定傳回Supplier中的随機值,因為Optional.empty()包含的值就是null。
Supplier總結:
- Supplier是一個供給型的接口,其中的get方法用于傳回一個值。
- Supplier也有很多的變種,例如IntSupplier、LongSupplier與BooleanSupplier等
四、Predicate
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);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
該接口将jdk1.8中接口的變化展現的淋漓盡緻,接口不再“純粹”了,可以有預設方法與靜态方法了,下次面試再問道,就得分情況喽,哭出聲。
要了解一個接口,我們就去實作它的方法。
Predicate<Integer> predicate=i->i>5;
System.out.println(predicate.test(1));
很明顯,輸出是false。等等,既然可以進行判斷,那和Stream.filter()有沒有關系呢?
Stream<T> filter(Predicate<? super T> predicate);
果然是有關系的,啧啧啧,我這敏銳的嗅覺。那我們把Predicate對象傳入filter試試?
List<Integer> list= Arrays.asList(1,2,3,4,5,6,7,8);
list.stream().filter(i->i>5).forEach(System.out::print);
很簡單,輸出是678。
Predicate總結:
- Predicate是一個判斷型的接口,用一個test方法去測試傳入的參數。
- 當然,Predicate也有對應的變種。
五、Function
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;
}
}
重點關注它的apply方法,現在就去實作它,并将之傳入進Stream.map()方法中試試。
public class TestFunction {
static class Student{
String name;
Integer id;
public Student(String name, Integer id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public Integer getId() {
return id;
}
}
public static void main(String[] args) {
List<Student> list= Arrays.asList(new Student("jack",1),new Student("tom",2));
Function<Student,Integer> function= Student::getId;
list.stream().map(function).forEach(System.out::print);
}
}
輸出12,可以看得出,Function中的apply方法将Student類型的資料轉化為對應id的Integer類型的資料。
- Function是一個轉換型的接口,其中的apply可以将一種類型的資料轉化成另外一種類型的資料。
- Function的變種就更多了。