快速理解Consumer、Supplier、Predicate與Function
一、前言
這幾個(gè)接口都處在java.util.function包下,Consumer(消費(fèi)型),Supplier(供給型)、Predicate(判斷型)與Function(轉(zhuǎn)換型),暫時(shí)不理解他們的類(lèi)型沒(méi)關(guān)系。
如果對(duì)Lambda不怎么理解的同學(xué),可以先移步到我的另外一篇文章對(duì)Lambda的理解
二、Consumer
Consumer是一個(gè)消費(fèi)型的接口,它接收一個(gè)西瓜,然后對(duì)這個(gè)西瓜進(jìn)行消費(fèi),連西瓜籽都不帶留下的。
先看Consumer接口的源碼,有一個(gè)未實(shí)現(xiàn)的抽象方法,和一個(gè)默認(rèn)方法(jdk1.8之后,接口里面可以有默認(rèn)方法和靜態(tài)方法)。
@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); };
}
}
我們只在意這個(gè)accept方法,接收一個(gè)泛型參數(shù),不返回任何值。ok,我們來(lái)簡(jiǎn)單實(shí)現(xiàn)它
Consumer<Integer> consumer=new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
};
consumer.accept(1);
}
好了,用腳指頭想著,肯定是輸出1了。
接下來(lái)我們使用lambda表達(dá)式來(lái)對(duì)此匿名內(nèi)部類(lèi)進(jìn)行改寫(xiě)。此時(shí)該lambda的類(lèi)型就是Consumer類(lèi)型。
consumer=i-> System.out.println(i);
當(dāng)然我們也可以使用方法引用
consumer=System.out::println;
在Stream類(lèi)中,我們發(fā)現(xiàn)常用的forEach接口接收一個(gè)Consumer類(lèi)型的參數(shù),源碼如下
void forEach(Consumer<? super T> action);
二話不說(shuō),我們將consumer傳入forEach中,來(lái)實(shí)現(xiàn)遍歷集合的操作。
List<Integer> list= Arrays.asList(1,2,3,4,5);
Consumer<Integer> consumer= System.out::println;
list.stream().forEach(consumer);
將中間consumer對(duì)象去掉呢,代碼會(huì)變得更加簡(jiǎn)潔。咦,到這里,是不是有一種似曾相識(shí)的感覺(jué),原來(lái)是這樣演變來(lái)的。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.stream().forEach(System.out::println);
Consumer總結(jié):
Consumer接口是一個(gè)消費(fèi)型的接口,只要實(shí)現(xiàn)它的accept方法,就能作為消費(fèi)者來(lái)輸出信息。
lambda、方法引用都可以是一個(gè)Consumer類(lèi)型,因此他們可以作為forEach的參數(shù),用來(lái)協(xié)助Stream輸出信息。
Consumer還有很多變種,例如IntConsumer、DoubleConsumer與LongConsumer等,歸根結(jié)底,這些變種其實(shí)只是指定了Consumer中的泛型而已,方法上并無(wú)變化。
三、Supplier
Supplier是一個(gè)供給型的接口,我們可以無(wú)條件的從它這里獲取東西。
@FunctionalInterface
public interface Supplier<T> {
T get();
}
我們不需要為get方法傳入任何參數(shù),就能獲得一個(gè)結(jié)果,這不是白嫖嗎?那我想要一個(gè)隨機(jī)數(shù)
Supplier<Double> supplier=()->new Random().nextDouble();
//當(dāng)然也可以使用方法引用
Supplier<Double> supplier1= Math::random;
System.out.println(supplier.get());
下一步,Supplier可以哪些地方呢,畢竟是可以白嫖的,誰(shuí)不喜歡呢?我們看看Supplier在Optional中的應(yīng)用。
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
該方法接收Supplier類(lèi)型的參數(shù),當(dāng)Optional內(nèi)部的value為空時(shí),才會(huì)返回Supplier中的值。例如
Optional<Double> optional=Optional.empty();
Supplier<Double> supplier=()->new Random().nextDouble();
optional.orElseGet(supplier);
這必定返回Supplier中的隨機(jī)值,因?yàn)镺ptional.empty()包含的值就是null。
Supplier總結(jié):
Supplier是一個(gè)供給型的接口,其中的get方法用于返回一個(gè)值。
Supplier也有很多的變種,例如IntSupplier、LongSupplier與BooleanSupplier等
四、Predicate
Predicate是一個(gè)判斷型接口,看看它的源碼。
@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中接口的變化體現(xiàn)的淋漓盡致,接口不再“純粹”了,可以有默認(rèn)方法與靜態(tài)方法了,下次面試再問(wèn)道,就得分情況嘍,哭出聲。
要理解一個(gè)接口,我們就去實(shí)現(xiàn)它的方法。
Predicate<Integer> predicate=i->i>5;
System.out.println(predicate.test(1));
很明顯,輸出是false。等等,既然可以進(jìn)行判斷,那和Stream.filter()有沒(méi)有關(guān)系呢?
Stream<T> filter(Predicate<? super T> predicate);
果然是有關(guān)系的,嘖嘖嘖,我這敏銳的嗅覺(jué)。那我們把Predicate對(duì)象傳入filter試試?
List<Integer> list= Arrays.asList(1,2,3,4,5,6,7,8);
list.stream().filter(i->i>5).forEach(System.out::print);
很簡(jiǎn)單,輸出是678。
Predicate總結(jié):
Predicate是一個(gè)判斷型的接口,用一個(gè)test方法去測(cè)試傳入的參數(shù)。
當(dāng)然,Predicate也有對(duì)應(yīng)的變種。
五、Function
Function是一個(gè)功能型的接口,用于將一種類(lèi)型的數(shù)據(jù)轉(zhuǎn)化為另外一種類(lèi)型的數(shù)據(jù)。
@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;
}
}
重點(diǎn)關(guān)注它的apply方法,現(xiàn)在就去實(shí)現(xiàn)它,并將之傳入進(jìn)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,可以看得出,F(xiàn)unction中的apply方法將Student類(lèi)型的數(shù)據(jù)轉(zhuǎn)化為對(duì)應(yīng)id的Integer類(lèi)型的數(shù)據(jù)。
Function總結(jié):
Function是一個(gè)轉(zhuǎn)換型的接口,其中的apply可以將一種類(lèi)型的數(shù)據(jù)轉(zhuǎn)化成另外一種類(lèi)型的數(shù)據(jù)。
Function的變種就更多了。
六、總結(jié)
首先只要記住這四個(gè)接口的類(lèi)型,Consumer(消費(fèi)型)、Supplier(供給型)、Predicate(判斷型)與Function(轉(zhuǎn)換型),
再記住他們對(duì)應(yīng)的抽象方法Consumer(accpet)、Supplier(get)、Predicate(test)與Function(apply)