之前我们一提到Java8新特性,两个常提的stream和Lambda表达式,实际还有一个大的方向就是函数接口。但是stream和Lambda表达式,使用场景也比较多,基本一推出来,基本日常场景就能用到。但是函数接口就不一样,他需要他自己的使用场景,如果不理解,只背他的定义,实际没啥帮助。这里我就通过介绍使用场景的方式来说一下Supplier函数接口。

一 使用场景

我们日常会用到泛型,java.util.function.Supplier 接口仅包含一个无参的方法: T get() 。用来获取一个泛型参数指定类型的对象数据。

那就只有一个get方法,有什么可以用到的场景,其实就是封装对象,这里我有两个场景可以介绍一下。

第一种,redis保存数据,大家都有共同的属性,过期单位和设置过期时间,但是里面的对象不确定,这里我们可以将对象以泛型的形式,作为suplier构成。

public class RedisSupplier <T> {

    private int expire;

    private TimeUnit timeUnit;

    Supplier<T> supplier;

    public int getExpire() {
        return expire;
    }

    public void setExpire(int expire) {
        this.expire = expire;
    }

    public TimeUnit getTimeUnit() {
        return timeUnit;
    }

    public void setTimeUnit(TimeUnit timeUnit) {
        this.timeUnit = timeUnit;
    }

    public Supplier<T> getSupplier() {
        return supplier;
    }

    public void setSupplier(Supplier<T> supplier) {
        this.supplier = supplier;
    }

    public RedisSupplier(int expire, TimeUnit timeUnit, Supplier<T> supplier) {
        this.expire = expire;
        this.timeUnit = timeUnit;
        this.supplier = supplier;
    }

    public T get() {
        return this.supplier.get();
    }

}

第二种,就是我们工作流,对应taskid这些都是固定的,对应不同的工作流参数,我们需要一个泛型管理起来。

protected abstract void updateDetail(Supplier<? extends InStorageBaseParam<?>> supplier, Long merchantId, boolean isApproval, InStorageOrderDO inStorageOrderDO, Attribute.ArticleAttribute attribute);
二 如何使用工作流

看Supplier里面只有一个get方法,获取一个泛型对象,那怎么把这个对象塞进去。

借助Lambda
借助lambad表达式直接塞对象进去。

Supplier<String> str = () -> "ZP handsome.";
        System.out.println("lambda Supplier :" + str.get() );

我们还可以借助对象里面的方法,进行调用。比如我们定义了一个makerMoney。

package com.example.test.j8.supplier;

/**
 * @Author: zhangpeng
 * @Description:
 * @Date: 2022/11/12
 */
public class Person {

    private Integer age;

    private String name;

    public Person(){}

    public Person(Integer age, String name){
        this.age = age;
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


    public String  makerMoney(){
        return "  make money for live.  ";
    }
}

然后调用(实际::双冒号就是调用方法的意思)

Supplier<String> str1 = person::makerMoney;
        System.out.println(" Non static method Supplier :" + str1.get());

构造方法
构造方法是现实中用的最多的一种方式,向Supplier塞进对象。

//真正使用场景,就是传参一个对象进去
        PersonSupplier<String> ps1 = new PersonSupplier<>(18,"卖课",()->"嘎嘎噶韭菜" );
        System.out.println(" construction method Supplier : " + ps1.getSupplier().get());

PersonSupplier<WebSite> ps2 = new PersonSupplier<>(18,"卖课",()->new WebSite("XX课程","高并发") );
        System.out.println(" construction method Supplier : " + ps2.getSupplier().get().getWebSiteName() + ps2.getSupplier().get().getTarget() );

具体代码参见我的GitHub对应目录
以上演示的完整代码

扩展

实际上和Supplier还有一个对应相反的一个函数接口,Consumer,听名字上可以这么区分,Supplier生产者,该接口定义一个无参的get方法,() -> T,如果不接受入参,直接为我们生产一个指定的结果,那么就可以用Supplier。

生产一个指定的结果

Conusmer消费者,该接口定义了一个void accept(T)的抽象方法,其函数描述符为 (T) -> void,如果你需要一个操作某一对象,但无需返回的的函数式接口,那么就可以使用Consumer接口。