Java 8的Consumer,Supplier,Predicate,Function四个接口简单理解

这几个接口都是在java.util.function包下

Consumer

这是一个消费型的接口,通过传入参数,然后输出值.

 /**
     * Consumer实例
     * 该接口有一个accept方法需要传递一个参数,还有一个默认方法
     */
    @Test
    public void test03(){
        Consumer<String> stringConsumer = new Consumer<String>(){

            /**
             * Performs this operation on the given argument.
             *
             * @param s the input argument
             */
            @Override
            public void accept(String s) {
                // 这里可以对传递进来的参数 进行操作
               // s="我是改写的参数";
                System.out.println(s);
            }
        };

        stringConsumer.accept("我是传递的参数");
        System.out.println("==============================");
        // Stream流的forEach使用的就是 Consumer类型的参数  Consumer<? super T> action
        Stream<String> stream = Stream.of("aaa", "bbb", "ddd", "ccc", "fff");
        // 这就意味着我们可以使用 Consumer接口来对于stream流的数据进行过滤或者操作
       // stream.forEach(stringConsumer);
        // 例子  有个要点,就是stream流只能被消费一次,即每次创建的steam流只能被用一次,所以上面的那个需要注释掉或者再次创建一个steam流
        System.out.println("==============================");
        stream.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                // 对传递的字符串进行操作
                if(s.equals("aaa")){
                    s="我是aaa";

                }
                System.out.println(s);
            }
        });
        System.out.println("==============================");
        Stream<String> stream02= Stream.of("aaa", "bbb", "ddd", "ccc", "fff");
        // stream02.forEach(System.out::print);
        // 使用lambda表达式输出,这就是我们常用的那种,本质就是传递一个实现Consumer接口,重写其中的accept方法
       // Consumer<String> consumer1 = (s) -> { System.out.println(s); };
        stream02.forEach(s -> {
            System.out.println(s);
        });
        
    }

小结

● Consumer是一个接口,其主要方法就是重写accept方法,就可以输出信息或者对信息进行操作
● lambda表达式就是隐式的实现接口来去作为Stream.forEach的参数 实现遍历.

Supplier

这个接口是一个供给型的接口,可以用来存储数据,

 /**
     * Supplier接口实例,相当于一个容器存储值
     */
    @Test
    public void test04() throws Throwable {
        // Supplier接口只有一个get方法 没有参数,
        Supplier<Double> supplier = Math::random;
        Object o = supplier.get();
        System.out.println(o);
        // 为什么要说他是一个容器呢?
        Supplier<String> str = new Supplier<String>() {
            @Override
            public String get() {
                return "我是一个容器";
            }
        };
        String s = str.get();
        System.out.println(s);
        // 看上去 的确像个容器  可以返回任意类型的对象 和集合和map的确有些类似
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
        // 获得一个Optional对象   发现Optianal对象也有一个get方法
        Optional<Integer> first = stream.filter(a -> a > 3).findFirst();
        if(first.isPresent()){ // isPresent方法是判断该对象是否为空
            Integer integer = first.get();
            System.out.println(integer);
        }
        System.out.println("==============================================");
        // 但是 Optional对象并没有实现Supplier接口,他的有一些方法需要 Supplier 类型的参数
        // orElseGet()方法 如果Supplier类型的参数返回的与 Optional对象(值为4)不一致就返回Optional对象中的数值,
        // 如果 Optional对象是null就返回Supplier类型的参数返回的值(值为 1)  直接点开orElseGet()方法也能清晰的看到该方法写了一个三元运算式
      Integer integer =   first.orElseGet(new Supplier<Integer>() {
            @Override
            public Integer get() {
                return 1;
            }
        });
        // 下面那个方法 是 如果first 为null 就将参数 other 设置进去,否则就返回 first 的值
        Integer integer1 = first.orElse(2);
        System.out.println(integer);
        System.out.println(integer1);
        // 这个方法就是如果 first2 为 null就抛出异常    我们这里就把first2做成null
        Stream<Integer> stream2 = Stream.of(1, 2, 3, 4, 5, 6);
        Optional<Integer> first2 = stream2.filter(a -> a > 7).findFirst();
        first2.orElseThrow(new Supplier<Throwable>() {
            @Override
            public Throwable get() {
                return new Exception("我是一个异常哦");
            }
        });

    }

小结

● Supplier 可以理解为一个只能装一个对象的容器,可以跟optional类型的对象配合
● 该接口只有一个get()方法比较简单,还有一些跟这个类似的接口,使用方法一样
● IntSupplier 、DoubleSupplier 、LongSupplier 、BooleanSupplier

疑问

为什么不直接使用值,非得创建一个接口做一个取值操作?感觉有点多此一举
或许这就是面向接口编程,可以将任意参数都转换为该类型的接口,去其他的类的方法进行统一入参,

面向接口编程定义: 面向接口编程(Interface Oriented Programming:OIP)是一种编程思想,接口作
为实体抽象出来的一种表现形式,用于抽离内部实现进行外部沟通,最终实现内部变动而不影响外部与其他实现交互,可以理解成按照这种思想来设计编程的方式就可以称为面向接口编程。

不过我也看到一个比较有意思的回答算是回答了这个问题,下面是地址,可以去看一下
链接: 在Java中使用Supplier的优势是什么?

我自己也对这个问题进行了思考,写了一个小demo

package top.oneyi;
import java.util.function.Supplier;

/**
 * 汽车父类
 */
public class Vehicle {
    private String name;

    private Integer age;

    private boolean flag;

    public void driver() {
        System.out.println("我在开" + this.name + "兜风");
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

// 奔驰类
class BenChi extends Vehicle {
    public BenChi() {

    }
    public BenChi(String name) {
        this.setName(name);
    }
    public BenChi(String name,Integer age) {
        this.setName(name);
        this.setAge(age);
    }
    // 复写父类方法
    public void driver() {
        System.out.println("尊贵的奔驰车主,欢迎使用我们的" + super.getName() + "兜风");
    }

    public void isfix(Integer age){
       if(age > 6){
           System.out.println(this.getName() +"需要修理了");
       }else{
           System.out.println(this.getName() +"不需要修理");
       }
    }
}

// 宝马类
class BaoMa extends Vehicle {
    public BaoMa() {

    }

    public BaoMa(String name,Integer age) {
        this.setName(name);
        this.setAge(age);
    }
    public BaoMa(String name) {
        this.setName(name);
    }
    // 复写父类方法
    public void driver() {
        System.out.println("尊贵的宝马车主,欢迎使用我们的" + super.getName() + "兜风");
    }
    public void see(){
        System.out.println("观看" + super.getName());
    }

    public void isfix(Integer age){
        if(age > 5){
            System.out.println("该车需要修理了");
        }
    }
}

class demo {
    /**
     * 使用Supplier 作为参数
     * @param car
     * @return
     */
    public static Vehicle diver(Supplier<? extends Vehicle> car) {
        return car.get();
    }

    /**
     * 使用父类作为参数
     * @param car
     * @return
     */
    public static void diver02(Vehicle car) {
         car.driver();

    }

    /**
     * 中间层需要 新加车辆是否需要修理方法
     * @param car
     */
    public static void diver03(Vehicle car) {
        BenChi car1 = (BenChi) car;
        car1.isfix(car1.getAge());

    }
}

class test{
    public static void main(String[] args) {
        demo.diver(new Supplier<Vehicle>() {
            @Override
            public Vehicle get() {
                return new BenChi("奔驰600");
            }
        }).driver();
        demo.diver(()->{return new BenChi("奔驰600");}).driver();

        demo.diver02(new BenChi("奔驰300"));
        // 使用Supplier接口可以使用lambda表达式,让代码看起来更加简洁
        System.out.println("==============================");
        demo.diver(()->{
            // 使用Supplier接口可以重写get方法来对Vehicle对象进行属性变动
            BenChi benChi = new BenChi("奔驰600");
            if(benChi.getName().equals("奔驰600")){
                benChi.setName("奔驰300");
            }
            return benChi;
        }).driver();
        System.out.println("==============================");
        // 看下面一个例子
        // 将设我们的车现在加了一个新的属性age,

        demo.diver(()->{
            // 使用Supplier接口可以重写get方法来对Vehicle对象进行属性变动
            BenChi benChi = new BenChi("奔驰600");
            benChi.isfix(7);
            return benChi;
        });
        // 这时候发现,BenChi,自己特有的方法父类不能实现只能进行强制转换,可以在之前的方法进行修改 或者直接新写一个方法
        demo.diver03(new BenChi("奔驰300",7));
        // 弊端出现了,如果Vehicle新加属性,并且子类对于该属性进行方法扩展,使用父类来当作参数的类的方法就需要重写,或者新加方法, 这样相当源代码就需要更改,风险很大,
        // 使用 Supplier 接口则是自己使用lambda表达式自己改变对应的方法,好处是中间层 demo不需要做成改变,把这个过程抽象成三层,第一层为 父类和子类  第二层是供应商类 第三层为客户类调用方
        // 当第一层做出改变,使用接口则第二层供应商类无需做出改变,第三层自己根据第一层的改变 自己改变自己的调用方式 或许这就是 Supplier 叫供给型接口的原因
    }
}

在这里插入图片描述

经过上面的demo,我也能理解上面那个链接最后解释的例子.第一层是属于汽车制造业,第二层是4S店,第三层是车主就像最后加的那个age属性,4s店肯定不能随时知道你的车辆情况,他也知道不了,他只知道卖车,还有就是打电话让你保养.只能用户是否知道自己的车是否需要修理(是否爆胎啊,撞到花池啥的).

Predicate

Predicate 接口是一个谓词型接口,返回值为布尔类型
这个比较简单就是返回布尔类型的

   /**
     * Predicate接口 只有一个test方法需要重写 其他都是默认方法
     */
    @Test
    public void test05(){
        Predicate<Integer> predicate = new Predicate<Integer>() {
            @Override
            public boolean test(Integer integer) {
                return integer > 5;
            }
        };
        boolean test = predicate.test(8);
        System.out.println(test);
        System.out.println("=============================");
        // lambda表达式
        predicate =  (t) -> t > 5;
        System.out.println(predicate.test(3));
        System.out.println("=============================");
        // Stream流中的filter参数类型就是 Predicate  大于 5的都会输出 
        Stream<Integer> stream = Stream.of(1, 23, 3, 4, 5, 56, 6, 6);
        stream.filter(predicate).forEach(System.out::println);

    }

小结

该接口就是起一个判断的作用,就没有什么其他作用了

Function

Function接口是一个功能型的接口,它的一个作用就是转换作用,将输入数据转换成另一种形式的输出数据.

/**
     * Function 接口是一个功能型接口,
     * 它的一个作用就是转换作用,将输入
     * 数据转换成另一种形式的输出数据。
     */
    @Test
    public void test06(){
        //泛型的第一个参数是转换前的类型,第二个是转化后的类型
            Function<String,Integer> fn = new Function<String,Integer>(){
                @Override
                public Integer apply(String s) {
                    return s.length();
                }
            };
            // 使用Stream流的map方法将字符串转换为字符串的长度
        Stream<String> stream = Stream.of("aaa", "bbbbb", "ccccccv");
        stream.map(fn).forEach(System.out::println);
        System.out.println("=============================");
        Stream<String> stream2= Stream.of("aaa", "bbbbb", "ccccccv");
        List<Integer> list = stream2.map(s -> {
            if(s.length() > 4){
                return s.length();
            }else{
                return 0;
            }
        }).collect(Collectors.toList());
        list.forEach(System.out::println);
    }

Stream流的map方法就是用的这个类型的参数,其实也都是过滤数据用的,除了这个接口还有其他的类似的接口
IntFunction 、DoubleFunction 、LongFunction 、ToIntFunction 、ToDoubleFunction 、DoubleToIntFunction 等等,使用方法和上面一样。
这四个接口大概就讲完了,使用倒是不难,就是很抽象,大家可以去看原文章,写的也很好.

链接: 参考文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值