前言:
在Java8支持Lambda表达式以后,为了满足 Lambda 表达式的一些典型使用场景,JDK为我们提供了大量常用的函数式接口。它们主要在 java.util.function 包中,下面简单介绍几个其中的接口及其使用示例。
Supplier接口
Supplier 接口是对象实例的提供者,定义了一个名叫 get 的抽象方法,它没有任何入参,并返回一个泛型T对象, 具体源码如下:
1 2 3 4 5 6 |
package java.util.function;
@FunctionalInterface public interface Supplier<T> { T get(); } |
源码比较简单,我们来个例子。这是之前提过的表示口罩的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
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 的实例:
1 |
Supplier<Mask> supplier = () -> new Mask( "3M" , "N95" ); |
用它来创建品牌为3M、类型为N95的 Mask 实例:
1 2 |
Mask mask = supplier.get(); System.out.println( "Brand: " + mask.getBrand() + ", Type: " + mask.getType()); |
运行结果如下:
Brand: 3M, Type: N95
特别需要注意的是,本例中每一次调用 get 方法都会创建新的对象。
Consumer接口
Consumer 接口是一个类似消费者的接口,定义了一个名叫 accept 的抽象方法,它的入参是一个泛型T对象,没有任何返回(void),主要源码如下:
1 2 3 4 5 6 |
package java.util.function;
@FunctionalInterface public interface Consumer<T> { void accept(T t); } |
结合上面的 Supplier 接口, 我们来个例子:
1 2 3 4 5 |
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
Predicate接口
Predicate 接口是判断是与否的接口,定义了一个名叫 test 的抽象方法,它的入参是一个泛型T对象,并返回一个boolean类型,主要源码如下:
1 2 3 4 5 6 |
package java.util.function;
@FunctionalInterface public interface Predicate<T> { boolean test(T t); } |
结合上面的 Supplier 接口,我们来个例子:
1 2 3 4 5 |
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
Function接口
Function 接口是对实例进行处理转换的接口,定义了一个名叫 apply 的抽象方法,它的入参是一个泛型T对象,并返回一个泛型R对象, 主要源码如下:
1 2 3 4 5 6 |
package java.util.function;
@FunctionalInterface public interface Function<T, R> { R apply(T t); } |
结合上面的 Supplier 接口,我们来个例子:
1 2 3 4 5 |
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
BiFunction接口
Function 接口的入参只有一个泛型对象,JDK还为我们提供了两个泛型对象入参的接口: BiFunction 接口, 主要源码如下:
1 2 3 4 5 6 |
package java.util.function;
@FunctionalInterface public interface BiFunction<T, U, R> { R apply(T t, U u); } |
我们可以用 BiFunction 接口传入两个 String 直接创建 Mask 实例:
1 2 3 |
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
基本数据类型
以上介绍的几个常用的函数式接口入参和返回,都是泛型对象的,也就是必须为引用类型。当我们传入或获取的是基本数据类型时,将会发生自动装箱和自动拆箱,带来不必要的性能损耗,比如:
1 2 |
Supplier<Long> supplier = () -> System.currentTimeMillis(); long timeMillis = supplier.get(); |
在上面例子里,发生了一次自动装箱(long被装箱为Long)和一次自动拆箱(Long被拆箱为long),如何避免这种不必要的性能损耗呢?JDK为我们提供相应的函数式接口,如 LongSupplier 接口,定义了一个名叫 getAsLong 的抽象方法,签名是 () -> long 。
上面的例子可以优化为:
1 2 |
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 |
到此这篇关于Java Lambda表达式常用的函数式接口的文章就介绍到这了,更多相关Lambda函数式接口内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
原文链接:https://blog.51cto.com/u_6740480/5180488
查看更多关于Java Lambda表达式常用的函数式接口的详细内容...