美文网首页
Lambda表达式-四大函数式接口

Lambda表达式-四大函数式接口

作者: 徒手說梦话 | 来源:发表于2020-09-08 20:25 被阅读0次

    Java语言自发布以来,从Java1.1一直到现在的Java14,Java通过不停的增加新功能,使得这门语言不断得到良好的提升。

    其中比较具有重要里程碑的版本,如JDK5.0,提供给我们诸如增强for循环,可变参数列表,静态导入,枚举类型定义,自动装箱拆箱,泛型等一些列优秀的功能。

    以及后来的jdk7.0,提供犹如二进制字面量,数字常量下划线,Switch运算中String类型的引入,try-with-resource的资源自动释放等,都给我们带来了很方便的操作和极大的便利。

    接口方法

    JDK1.8之前,接口中的方法,都必须是抽象方法。实现接口的类,必须实现接口中定义的每一个抽象方法。

    由于JDK1.8的API,在已有的接口上,新增了很多的新方法,这种新增变化带来的问题,正如上述的情况一样,也会给使用这些接口的老用户带来很多的不便。

    为了解决这个问题,JDK1.8中引入了一种新的机制:接口可以支持在声明方法的同时,提供实现。

    主要通过两种方式可以完成这种操作:

    1. 默认方法
    2. 静态方法

    默认方法

    JDK1.8中,接口里面可以定义默认方法。

    interface InterfaceName{

    ​ default returnType methodName(arg-list){ }

    }

    新时代的程序员:lambda表达式(方法引用)、链式编程、函数式接口、Stream流式计算

    interface FirstInterface{
        //传统定义,抽象方法,没有方法体。
        voidbefore();
        //默认方法
        defaultvoidtest() {
            System.out.println("Default method in FirstInterface");
        }
    }
    classFirstClass implements FirstInterface{
        //所有实现类必须实现接口中未实现的方法。
        @Override
        public void before() {
            System.out.println("我是FirstInterface中的抽象方法,所有实现类必须实现!");
        }
    }
    public class DefaultMethod {
        publicstaticvoidmain(String[] args) {
            FirstClass fc=newFirstClass();
            fc.test(); //此处输出Default method in FirstInterface,对于默认方法,如果实现类中没有实现就是用默认的。
            fc.before(); //此处输出我是FirstInterface中的抽象方法,所有实现类必须实现。
        }
    }
    

    默认方法存在的两大优势

    1. 可以让接口更优雅的升级,减少使用人员操作的负担不必随着接口方法增加,从而修改实现代码,因为默认方法在子类中可以不用实现
    2. 可以让实现类中省略很多不必要方法的空实现

    方法调用的判断规则:

    1. 类中声明的方法优先级最高。类或父类中,声明的方法要高于任何默认方法的优先级
    2. 如果无法依据第一条进行判断,那么子接口的优先级更高例如,如果 B 接口继承了 A接口,那么 B 就比 A 更加具体,优先级更高所以,在上述例子中,B是子接口,优先级别更高,调用test方法后输出:defaut method test in B.
    3. 最后,如果还是无法判断,那么继承了多个接口的类,必须通过实现(重写)方法来确定方法的调用

    静态方法

    JDK1.8中,接口里面可以定义静态方法。

    和类中定义的静态方法类似,接口中的静态方法可以直接通过接口名.静态方法名的形式进行访问。

    语法:

    interface InterfaceName{ 
        static returnType methodName(arg-list){
            //代码实现
        }
    }
    

    访问:

    InterfaceName.methodName();

    注意,接口中的静态方法,只能使用当前接口的名字来调用

    Lambda使用

    虽然Lambda表达式在java中的实际意义,是对一个接口的实现,但并不是任何接口都可以使用Lambda表达式来进行实现。

    原因也很简单,一个Lambda表达式,只是描述了一个函数的参数列表、函数主体、返回类型,那么它顶多是对接口中的一个抽象方法的实现,如果接口中有多个抽象方法呢?

    很显然,这时候一个Lambda表达式是无法表示为这个接口的实现,因为无法实现接口中多个抽象方法。

    所以,接口中==有且只有==一个抽象方法的时候,才可以使用Lambda表达式来对其进行实现。

    方法引用https://www.cnblogs.com/wuhenzhidu/p/10727065.html

    函数式接口: 只有一个方法的接口;简化编程模型

    @FunctionalInterface 
    public interface Runnable {    
        public abstract void run(); 
    } 
    // JDK1.5:泛型、枚举、反射
    // JDK1.8:lambda表达式、链式编程、函数式接口、Stream流式计算 
    // 超级多FunctionalInterface 
    // 简化编程模型,在新版本的框架底层大量应用!
    // foreach(消费者类的函数式接口)
    

    JDK1.8中,针对函数式接口,新增了一个注解@FunctionalInterface,用来检查被标注的接口,是不是一个函数式接口,如果不是,那么编译器会报错。

    但是,该注解不是必须要用的,它只是会让编辑器帮我们检查一下而已,以免出现接口中抽象方法的个数不是1的情况。

    例如,编译通过

    @FunctionalInterface
    interface Action{
        int action(int a,int b);
    }
    

    例如,编译报错

    @FunctionalInterface
    interface Action{
        int action(int a,int b);
        int action();
    }
    

    Lambda语法

    Lambda表达式的格式为:() -> {}

    • ()表示需要表达的抽象方法参数列表
    • ->后面跟的是函数主体
    • {}函数主体,表达式的返回值,由这个函数主体中代码来决定

    个人理解:Lambda表达式更专注抽象方法种的形参和行为,Lambda传入的参数类型可以省略但是传入的参数只是一个形参,并不是需要传入的实际数据,用于函数主体调用

    image-20200310210723763.png
    代码测试:

    Function

    Function函数式接口

    给Function<T ,R>接口传入两个参数,返回的参数为传入的第二个参数的类型,而第一个形参作为实参传入apply方法。

    QQ截图20200908201803.png
    package com.lxy.function;
    import java.util.function.Function;
    /** 
    * Function 函数型接口, 有一个输入参数,有一个输出 
    * 只要是 函数型接口 可以 用 lambda表达式简化
    */
    public class Demo01 {    
        public static void main(String[] args) {
            //Function<String,String> function = new Function<String,String>() { 
            //            @Override
            //            public String apply(String str) { 
            //                return str; 
            //            } 
            //        };
            Function<String,String> function = (str)->{return str;};
            System.out.println(function.apply("asd"));   
        } 
    }
    
    package lambda;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.function.BiConsumer;
    import java.util.function.Function;
    
    public class TestFunction {
        public static void main(String[] args) {
            String name = "zhangsan";
            // 返回字符串的长度
            int oper = oper(name, str -> str.length());
            oper = oper(name, String::length);
    
            // 返回字符串的对应下标
            oper = oper(name, str -> (int) str.charAt(0));
    
            // 遍历数组
            Map<String ,Object> map = new HashMap();
            map.put("张铁林",50);
            map.put("陈一发",20);
            map.put("健字号",52);
            map.put("刘文治",33);
            map.forEach(new BiConsumer<String, Object>() {
                @Override
                public void accept(String s, Object o) {
                   // 具体行为
                }
            });
            map.forEach((str,obj) -> System.out.println(str+"->"+obj));
    
        }
        // 根据给定的字符串的,返回字符串对应具体条件的值.
        public static int oper(String str, Function<String, Integer> function){
            return function.apply(str);
        }
    }
    

    Predicate

    在使用该接口来做判断的时候,经常需要几个条件同时成立,或者其中一个条件成立,或者求反。

    在这种情况下,除了可以在代码中选择使用&&,||,!之外,也可以分别使用这三个方法来代替。

    • and()
    • or()
    • negate()

    断定型接口:有一个输入参数,返回值只能是 布尔值!

    QQ截图20200908202109.png
    package com.lxy.function;
    import java.util.function.Predicate;
    /**
    * 断定型接口:有一个输入参数,返回值只能是 布尔值! 
    */ 
    public class Demo02 {   
        public static void main(String[] args) {    
            // 判断字符串是否为空
            //        Predicate<String> predicate = new Predicate<String>(){
            //            @Override
            //            public boolean test(String str) { 
            //                return str.isEmpty(); 
            //            } 
            //        };
            Predicate<String> predicate = (str)->{return str.isEmpty(); };        
            System.out.println(predicate.test(""));
        } 
    }
    
    public class PredicateTest {
        public static void main(String[] args) {
            // 普通创建数组方法
            Integer[] arrInteger = new Integer[]{1,20,15,8,9,31,40};
            // jdk1.8创建数组
            // 声明对此方法具体行为的引用,但并不是执行真正的方法
            ActionDemo01 action = (size)->{return new Integer[size];};
            action = Integer[]::new; // 与上面的效果是一样的,主要是上面new Integer[size],下面我们只需要声明就好了
            action.arr(10);
    
    
            // 条件1,数据大于10,第一种方式
            List<Integer> filter = filter(arrInteger, new Predicate<Integer>() {
                @Override
                public boolean test(Integer integer) {
                    return integer > 10;
                }
            });
            // 第二种方式
            filter = filter(arrInteger,(integer)->integer>10);
    
            // 条件2,数据大于10,并且能被2整除,方式1
            
            // 先写一个大于10断定式接口,之后再写一个能被二整除的断定式接口
            Predicate<Integer> action1 = (integer)->integer>10;
            Predicate<Integer> action2 = (integer)->integer%2==0;
            // 将两个断定式接口组合
            Predicate<Integer> p = action1.and(action2);
            filter = filter(arrInteger,p);
    
            // 方式2
            filter = filter(arrInteger,(integer -> integer>10 && integer%2==0));
    
        }
        //定义一个方法,用来过滤数组中所有符合要求的数据。
        public static List<Integer> filter(Integer[] val, Predicate<Integer> con) {
            List<Integer> list = new ArrayList<>();
            for(Integer i:val) {
                if(con.test(i)) {
                    list.add(i);
                }
            }
            return list;
        }
    }
    // 声明接口
    interface ActionDemo01{
        Integer[] arr(Integer size);
    }
    

    Consumer

    Consumer 消费型接口


    QQ截图20200908202321.png
    package com.lxy.function;
    import java.util.function.Consumer;
    /**
    * Consumer 消费型接口: 只有输入,没有返回值
    */
    public class Demo03 {    
        public static void main(String[] args) { 
            //        Consumer<String> consumer = new Consumer<String>() { 
            //            @Override
            //            public void accept(String str) {
            //                System.out.println(str); 
            //            } 
            //        };      
            Consumer<String> consumer = (str)->{System.out.println(str);};        
            consumer.accept("sdadasd");
        } 
    }
    
    
    public class TestConsumer {
        public static void main(String[] args) {
            Data[] datas = new Data[]{new Data("abc"),new Data("123"),new Data("这书评?"),new Data("放学了")};
            // 打印结果,方式1
            oper(datas, new Consumer<Data>() {
                @Override
                public void accept(Data data) {
                    System.out.println(data);
                }
            });
            // 打印结果,方式2,因为规定了消费者接口种的泛型为Data所有传入的参数具有Data的行为
            oper(datas,(data -> System.out.println(data.getVal())));
            // 打印结果,方式3,注意返回的是接口!
            Consumer<Data> con = System.out::println;
            oper(datas,con);
            
        }
    
        // 提供一个方法,用来针对给定的一组Data种的每个值进行操作
        public static void oper(Data[] data, Consumer<Data> consumer){
            for (Data d : data){
                consumer.accept(d);
            }
        }
    }
    
    class Data{
        private String val;
        public Data(String val) {
            this.val = val;
        }
        public String getVal() {
            return val;
        }
        public void setVal(String val) {
            this.val = val;
        }
    }
    

    Supplier 供给型接口

    QQ截图20200908202451.png
    package com.lxy.function;
    import java.util.function.Supplier;
    /**
    * Supplier 供给型接口 没有参数,只有返回值
    */ 
    public class Demo04 {  
        public static void main(String[] args) {
            //        Supplier supplier = new Supplier<Integer>() { 
            //            @Override 
            //            public Integer get() { 
            //                System.out.println("get()"); 
            //                return 1024; 
            //            }
            //        };
            Supplier supplier = ()->{ return 1024; };       
            System.out.println(supplier.get());    
        }
    }
    
    public class TestSupplier {
        public static void main(String[] args) {
            //生成一个1--100以内的随机奇数
            Supplier<Integer> sup = new Supplier<Integer>() {
                @Override
                public Integer get() {
                    int result ;
                    do {
                        result = (int)(Math.random()*100+1);
                    }while((result&1)==0);
                    return result;
                }
            };
            int[] vals = consumer(()->{
                int result ;
                do {
                    result = (int)(Math.random()*100+1);
                }while((result&1)==0); // 这个写发和result%2==0是一样的只不过效率更高
                return result;
            });
            System.out.println(Arrays.toString(vals));
        }
        //定义一个方法,用来生成10个符合要求的1--100以内的随机数.
        public static int[] consumer(Supplier<Integer> sup) {
            int[] a = new int[10];
            for(int i = 0;i<a.length;i++) {
                a[i] = sup.get();
            }
            return a;
        }
    }
    

    相关文章

      网友评论

          本文标题:Lambda表达式-四大函数式接口

          本文链接:https://www.haomeiwen.com/subject/tqpjektx.html