失去人性,失去很多;失去獸性,失去一切。——《三體》
在Java8支持Lambda表達式以后,為了滿足Lambda表達式的一些典型使用場景,JDK為我們提供了大量常用的函數式接口。它們主要在 java.util.function 包中,下面簡單介紹幾個其中的接口及其使用示例。
Supplier接口
Supplier
接口是對象實例的提供者,定義了一個名叫get
的抽象方法,它沒有任何入參,並返回一個泛型T對象,具體源碼如下:
package java.util.function;
@FunctionalInterface
public interface Supplier<T> {
T get();
}
源碼比較簡單,我們來個例子。這是一個之前提過的表示口罩的類:
package one.more.study;
/**
* 口罩
*/
public class Mask {
public Mask(String brand, String type) {
this.brand = brand;
this.type = type;
}
/**
* 品牌
*/
private String brand;
/**
* 類型
*/
private String type;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
下面我們使用Lambda表達式聲明一個Supplier
的實例:
Supplier<Mask> supplier = () -> new Mask("3M", "N95");
用它來創建品牌為3M、類型為N95的Mask
實例:
Mask mask = supplier.get();
System.out.println("Brand: " + mask.getBrand() + ", Type: " + mask.getType());
運行結果如下:
Brand: 3M, Type: N95
特別需要注意的是,本例中每一次調用get
方法都會創建新的對象。
歡迎關注微信公眾號:萬貓學社,每周一分享Java技術干貨。
Consumer接口
Consumer
接口是一個類似消費者的接口,定義了一個名叫accept
的抽象方法,它的入參是一個泛型T對象,沒有任何返回(void),主要源碼如下:
package java.util.function;
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
結合上面的Supplier
接口,我們來個例子:
Supplier<Mask> supplier = () -> new Mask("3M", "N95");
Consumer<Mask> consumer = (Mask mask) -> {
System.out.println("Brand: " + mask.getBrand() + ", Type: " + mask.getType());
};
consumer.accept(supplier.get());
首先使用Lambda表達式聲明一個Supplier
的實例,它是用來創建品牌為3M、類型為N95的Mask
實例;再使用Lambda表達式聲明一個Consumer
的實例,它是用於打印出Mask
實例的相關信息;最后Consumer
消費了Supplier
生產的Mask
。運行結果如下:
Brand: 3M, Type: N95
歡迎關注微信公眾號:萬貓學社,每周一分享Java技術干貨。
Predicate接口
Predicate
接口是判斷是與否的接口,定義了一個名叫test
的抽象方法,它的入參是一個泛型T對象,並返回一個boolean類型,主要源碼如下:
package java.util.function;
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
結合上面的Supplier
接口,我們來個例子:
Supplier<Mask> supplier = () -> new Mask("3M", "N95");
Predicate<Mask> n95 = (Mask mask) -> "N95".equals(mask.getType());
Predicate<Mask> kn95 = (Mask mask) -> "KN95".equals(mask.getType());
System.out.println("是否為N95口罩:" + n95.test(supplier.get()));
System.out.println("是否為KN95口罩:" + kn95.test(supplier.get()));
首先使用Lambda表達式聲明一個Supplier
的實例,它是用來創建品牌為3M、類型為N95的Mask
實例;再使用Lambda表達式聲明一個Predicate
的實例n95,它是用於判斷是否為N95口罩;再使用Lambda表達式聲明一個Predicate
的實例kn95,它是用於判斷是否為KN95口罩;最后分別用兩個Predicate
判斷Supplier
生產的Mask
。運行結果如下:
是否為N95口罩:true
是否為KN95口罩:false
歡迎關注微信公眾號:萬貓學社,每周一分享Java技術干貨。
Function接口
Function
接口是對實例進行處理轉換的接口,定義了一個名叫apply
的抽象方法,它的入參是一個泛型T對象,並返回一個泛型T對象,主要源碼如下:
package java.util.function;
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
結合上面的Supplier
接口,我們來個例子:
Supplier<Mask> supplier = () -> new Mask("3M", "N95");
Function<Mask, String> brand = (Mask mask) -> mask.getBrand();
Function<Mask, String> type = (Mask mask) -> mask.getType();
System.out.println("口罩品牌:" + brand.apply(supplier.get()));
System.out.println("口罩類型:" + type.apply(supplier.get()));
首先使用Lambda表達式聲明一個Supplier
的實例,它是用來創建品牌為3M、類型為N95的Mask
實例;再使用Lambda表達式聲明一個Function
的實例brand,它是用於獲取口罩的品牌;再使用Lambda表達式聲明一個Function
的實例type,它是用於獲取口罩的類型;最后分別用兩個Function
分析Supplier
生產的Mask
。運行結果如下:
口罩品牌:3M
口罩類型:N95
歡迎關注微信公眾號:萬貓學社,每周一分享Java技術干貨。
BiFunction接口
Function
接口的入參只有一個泛型對象,JDK還為我們提供了兩個泛型對象入參的接口:BiFunction
接口,主要源碼如下:
package java.util.function;
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
}
我們可以用BiFunction
接口傳入兩個String
直接創建Mask
實例:
BiFunction<String,String,Mask> biFunction = (String brand, String type) -> new Mask(brand, type);
Mask mask = biFunction.apply("3M", "N95");
System.out.println("Brand: " + mask.getBrand() + ", Type: " + mask.getType());
運行結果如下:
Brand: 3M, Type: N95
歡迎關注微信公眾號:萬貓學社,每周一分享Java技術干貨。
基本數據類型
以上介紹的幾個常用的函數式接口入參和返回,都是泛型對象的,也就是必須為引用類型。當我們傳入或獲取的是基本數據類型時,將會發生自動裝箱和自動拆箱,帶來不必要的性能損耗,比如:
Supplier<Long> supplier = () -> System.currentTimeMillis();
long timeMillis = supplier.get();
在上面例子里,發生了一次自動裝箱(long被裝箱為Long)和一次拆箱(Long被拆箱為long),如何避免這種不必要的性能損耗呢?JDK為我們提供相應的函數式接口,如LongSupplier
接口,定義了一個名叫getAsLong
的抽象方法,簽名是() -> long
。上面的例子可以優化為:
LongSupplier supplier = () -> System.currentTimeMillis();
long timeMillis = supplier.getAsLong();
類似這樣的接口還有很多,我為大家整理了一下:
Supplier相關的接口
接口名稱 | 方法名稱 | 方法簽名 |
---|---|---|
Supplier | get | () -> T |
BooleanSupplier | getAsBoolean | () -> boolean |
DoubleSupplier | getAsDouble | () -> double |
IntSupplier | getAsInt | () -> int |
LongSupplier | getAsLong | () -> long |
Consumer相關的接口
接口名稱 | 方法名稱 | 方法簽名 |
---|---|---|
Consumer | accept | (T) -> void |
DoubleConsumer | accept | (double) -> void |
IntConsumer | accept | (int) -> void |
LongConsumer | accept | (long) -> void |
ObjDoubleConsumer | accept | (T, double) -> void |
ObjIntConsumer | accept | (T, int) -> void |
ObjLongConsumer | accept | (T, long) -> void |
Predicate相關的接口
接口名稱 | 方法名稱 | 方法簽名 |
---|---|---|
Predicate | test | (T) -> boolean |
BiPredicate | test | (T, U) -> boolean |
DoublePredicate | test | (double) -> boolean |
IntPredicate | test | (int) -> boolean |
LongPredicate | test | (long) -> boolean |
Function相關的接口
接口名稱 | 方法名稱 | 方法簽名 |
---|---|---|
Function | apply | (T) -> R |
BiFunction | apply | (T, U) -> R |
DoubleFunction | apply | (double) -> R |
DoubleToIntFunction | applyAsInt | (double) -> int |
DoubleToLongFunction | applyAsLong | (double) -> long |
IntFunction | apply | (int) -> R |
IntToDoubleFunction | applyAsDouble | (int) -> double |
IntToLongFunction | applyAsLong | (int) -> long |
LongFunction | apply | (long) -> R |
LongToDoubleFunction | applyAsDouble | (long) -> double |
LongToIntFunction | applyAsInt | (long) -> int |
ToDoubleFunction | applyAsDouble | (T) -> double |
ToDoubleBiFunction | applyAsDouble | (T, U) -> double |
ToIntFunction | applyAsInt | (T) -> int |
ToIntBiFunction | applyAsInt | (T, U) -> int |
ToLongFunction | applyAsLong | (T) -> long |
ToLongBiFunction | applyAsLong | (T, U) -> long |
《死磕Lambda表達式》系列
- 死磕Lambda表達式(一):初識Lambda
- 死磕Lambda表達式(二):Lambda的使用
- 死磕Lambda表達式(三):更簡潔的Lambda
- 死磕Lambda表達式(四):常用的函數式接口
- 死磕Lambda表達式(五):Comparator復合
- 死磕Lambda表達式(六):Consumer、Predicate、Function復合
微信公眾號:萬貓學社
微信掃描二維碼
獲得更多Java技術干貨
