死磕Lambda表达式(四):常用的函数式接口
失去人性,失去很多;失去兽性,失去一切。——《三体》
在Java8支持Lambda表达式以后,为了满足Lambda表达式的一些典型使用场景,JDK为我们提供了大量常用的函数式接口。它们主要在 java.util.function 包中,下面简单介绍几个其中的接口及其使用示例。
Supplier接口
Supplier接口是对象实例的提供者,定义了一个名叫get的抽象方法,它没有任何入参,并返回一个泛型T对象,具体源码如下:
package java.util.function;
@FunctionalInterface
public interface Supplier {
    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 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 {
    void accept(T t);
}
 
结合上面的Supplier接口,我们来个例子:
Supplier supplier = () -> new Mask("3M", "N95");
Consumer 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 {
    boolean test(T t);
}
 
结合上面的Supplier接口,我们来个例子:
Supplier supplier = () -> new Mask("3M", "N95");
Predicate n95 = (Mask mask) -> "N95".equals(mask.getType());
Predicate 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 {
    R apply(T t);
}
 
结合上面的Supplier接口,我们来个例子:
Supplier supplier = () -> new Mask("3M", "N95");
Function brand = (Mask mask) -> mask.getBrand();
Function 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 {
    R apply(T t, U u);
}
 
我们可以用BiFunction接口传入两个String直接创建Mask实例:
BiFunction 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 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技术干货