• 死磕Lambda表达式(四):常用的函数式接口


    失去人性,失去很多;失去兽性,失去一切。——《三体》

    在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表达式》系列

    微信公众号:万猫学社

    微信扫描二维码

    获得更多Java技术干货

  • 相关阅读:
    信息系统项目管理师2020年下半年下午案例分析题及答案
    信息系统项目管理师2019年下半年下午案例分析题及答案
    系统集成项目管理工程师2021年上半年下午案例分析题及答案
    信息系统项目管理师2021年下半年下午案例分析题及答案
    信息系统项目管理师2019年上半年下午案例分析题及答案
    系统集成项目管理工程师2021年下半年下午案例分析题及答案
    信息系统项目管理师2021年上半年下午案例分析题及答案
    深入理解C#笔记(1)
    SqlBulkCopy批量插入数据
    SQL ROW_NUMBER() OVER函数的基本用法用法
  • 原文地址:https://www.cnblogs.com/heihaozi/p/12557522.html
Copyright © 2020-2023  润新知