zoukankan      html  css  js  c++  java
  • Lambda表达式

    1、Lamdba表达式

    1.1、Lambda表达式的概念

    Lambda表达式,从本质上来讲,是一个匿名函数。可以使用这个匿名函数,实现接口中的方法,从而简化代码。

    1.2、Lambda表达式的使用场景

    通常来讲,使用Lambda表达式,是为了简化接口实现的。

    关于接口实现,可以有很多种方式来实现。例如:设计借口的实现类、使用匿名内部类。但是lambda表达式,比这两种方式简单。

    package com.helen.lambda.main;
    
    import com.helen.lambda.funcationInterface.SingleReturnSingleParameter;
    
    /**
     * @Description
     * @Author BF <xu.rongchang@foxmail.com>
     * @Version 1.0
     * @Date 2021/6/3
     */
    public class Program {
        public static void main(String[] args) {
            interfaceImpl();
        }
    
    
        public static void interfaceImpl(){
            // 1、使用显示的实现类对象
            SingleReturnSingleParameterImpl parameter1 = new SingleReturnSingleParameterImpl();
            // 2、使用匿名内部类的方式
            SingleReturnSingleParameter parameter2 = new SingleReturnSingleParameter() {
                @Override
                public int test(int a) {
                    return a * a;
                }
            };
            // 3、使用lambda表达式的方式实现
            SingleReturnSingleParameter parameter3 = a ->  a * a;
         
            System.out.printf("显示的实现类对象: %d
    ",parameter1.test(10));
            System.out.printf("匿名内部类的方式: %d
    ", parameter2.test(10));
            System.out.printf("lambda表达式的方式: %d
    ", parameter3.test(10));
        }
    }
    
    
    class SingleReturnSingleParameterImpl implements SingleReturnSingleParameter{
        @Override
        public int test(int a) {
            return a * a;
        }
    }
    

    1.3、Lamdba表达式对接口的要求

    虽然说,lambda表达式可以在一定程度上简化接口的实现。但是,并不是所有的接口都可以使用lambda表达式来简洁实现的。

    lambda表达式毕竟只是一个匿名方法。当实现的接口中的方法过多或者过少的时候(一个都没有),lambda表达式都是不适用的。

    lambda表达式,只能实现函数式接口。

    1.4、函数式接口

    1.4.1、基础概念

    如果说,一个接口中,要求实现类必须实现的抽象方法,有且只有一个! 这样的接口,就是函数式接口。

    // 这个接口中,有且只有一个方法,是实现类必须实现的,因此是一个函数式接口。
    interface Test1 {
         void test();
    }
    
    // 这个接口中,实现类必须实现的方法,有两个! 因此不是一个函数式接口。
    interface Test2 {
        void test1();
        void test2();
    }
    
    // 这个接口中,实现类必须实现的方法,有零个!因此不是一个函数式接口。
    interface Test3 {
    
    }
    
    // 这个接口中,虽然没有定义任何方法,但是可以从父类中继承一个抽象方法,因此是一个函数式接口。
    interface Test4 extends Test1 {
    
    }
    
    // 这个接口,虽然里面定义了两个方法,但是 default 修饰的方法子类不是必须实现的。
    // 因此子类必须实现的方法也只有一个,所以此接口是一个函数式接口。
    interface Test5 {
        void test5();
        default void test2(){};
    }
    
    // 这个接口中的 toString 方法,是Object中定义的方法
    // 此时实现类在实现接口的时候,toString可以不重写!因为可以从父类Object中继承到!
    // 此时,实现类在实现接口的时候,有且只有一个接口需要实现,因此是一个函数式接口。
    interface Test6 {
        void test6();
        String toString();
    }
    

    1.4.2、@FunctionalInterface

    是一个注解,用在接口上,判断这个接口是否是一个函数式接口。如果是函数式接口,没任何问题。如果不是一个函数式接口,则会报错。功能类似于 @Override。

    @FunctionalInterface
    interface Test1 {
         void test();
    }
    

    2、Lambda表达式的语法

    2.1、Lambda表达式的基础语法

    Lambda表达式,其实本质来讲,就是一个匿名函数。因此在写lambda表达式的时候,不需要关心方法名是什么。

    实际上,我们在写lambda表达式的时候,也不需要关系返回值的类型。

    我们在写lambda表达式的时候,只需要关注两部分内容即可:参数列表方法体

    lambda表达式的基础语法:

    (参数) -> {
    	方法体
    };
    

    参数部分:方法的参数列表,要求和实现的接口中的方法参数一致,包括参数的数量和类型。

    方法体部分:方法的实现部分,如果接口中定义的方法有返回值,则在实现的时候,注意返回值的返回。

    -> : 分隔参数部分和方法体部分。

     public class BasicSyntax {
        public static void main(String[] args) {
            // 1、实现无参、无返回值函数式接口
            NoneReturnNoneParameter lambda1 = () -> {
                System.out.println("lambda 实现 无参、无返回值函数式接口");
            };
            lambda1.test();
    
            // 2、实现一个参数、无返回值的函数式接口
            NoneReturnSingleParameter lambda2 =  (int a) -> {
                System.out.println("lambda 实现 一个参数、无返回值的函数式接口。参数a的值为:"+a);
            };
            lambda2.test(100);
    
            // 3、实现多个参数、无返回值的函数式接口
            NoneReturnMultipleParameter lambda3 = (int a ,int b) -> {
                System.out.println("lambda 实现 多个参数、无返回值的函数式接口 参数a的值为:"+a+"    参数b的值为:"+b);
            };
            lambda3.test(100,200);
    
            // 4、实现有返回值、无参数的函数式接口
            SingleReturnNoneParameter lambda4 = () -> {
                System.out.println("lambda 实现 有返回值、无参数的函数式接口    返回值是:"+10);
                return 10;
            };
            int ret1 = lambda4.test();
            System.out.println("ret1 = " + ret1);
    
            // 5、实现有返回值、一个参数的函数式接口
            SingleReturnSingleParameter lambda5 = (int a) -> {
                System.out.println("lambda 实现 有返回值、一个参数的函数式接口    返回值是:"+a);
                return a;
            };
            int ret2 = lambda5.test(1998);
            System.out.println("ret2 = " + ret2);
            
            // 6、实现有返回值、多个参数的函数式接口
            SingleReturnMultipleParameter lambda6 = (int a ,int b) -> {
                System.out.println("lambda 实现 有返回值、多个参数的函数式接口");
                return a+b;
            };
            int ret3 = lambda6.test(100,200);
            System.out.println("ret3 = " + ret3);
        }
    }
    
    

    2.2、Lambda表达式的语法进阶

    在上述代码中,的确可以使用lambda表达式实现接口,但是依然不够简洁,有简化的空间。

    2.2.1、参数部分的精简

    • 参数的类型

      • 由于在接口的方法中,已经定义了每一个参数的类型是什么。而且在使用lambda表达式实现接口的时候,必须要保证参数的数量和类型需要和接口中的方法保持一致。因此,此时lambda表达式中的参数类型可以省略不写。

      • 注意事项:

        • 如果需要省略参数的类型,要保证:要省略,每一个参数的类型必须保证不写。绝对不能出现,有的参数类型省略了,有的参数类型没有省略。

          // 3、实现多个参数、无返回值的函数式接口
                  NoneReturnMultipleParameter lambda3 = ( a , b) -> {
                      System.out.println("lambda 实现 多个参数、无返回值的函数式接口 参数a的值为:"+a+"    参数b的值为:"+b);
                  };
                  lambda3.test(100,200);
          
    • 参数的小括号

      • 如果方法的参数列表中参数数量 有且只有一个 ,此时,参数列表的小括号是可以省略不写的。

      • 注意事项:

        • 只有当参数的数量是一个的时候,才可以省略不写。

        • 省略小括号的同时,必须要省略参数的类型。

          // 2、实现一个参数、无返回值的函数式接口
                  NoneReturnSingleParameter lambda2 =   a -> {
                      System.out.println("lambda 实现 一个参数、无返回值的函数式接口。参数a的值为:"+a);
                  };
                  lambda2.test(100);
          

    2.2.2、方法体部分

    • 方法体的大括号

      • 如果方法体有且只有一条语句时,方法体的大括号可以省略不写。

        // 2、实现一个参数、无返回值的函数式接口
                NoneReturnSingleParameter lambda2 =   a -> System.out.println("lambda 实现 一个参数、无返回值的函数式接口。参数a的值为:"+a);
                lambda2.test(100);
        
    • 返回关键字return

      • 如果方法体有且只有一条语句,并且就是返回语句时。return 关键字可以省略不写。

        //之前:
        // 6、实现有返回值、多个参数的函数式接口
                SingleReturnMultipleParameter lambda6 = (int a ,int b) -> {
                    return a * b;
                };
                int ret3 = lambda6.test(100,200);
                System.out.println("ret3 = " + ret3);
        
        // 精简之后:
        // 6、实现有返回值、多个参数的函数式接口
                SingleReturnMultipleParameter lambda6 = (int a ,int b) -> a * b;
                int ret3 = lambda6.test(100,200);
                System.out.println("ret3 = " + ret3);
        

    3、函数引用

    lambda表达式是为了简化接口实现的。在lambda表达式中,不应该出现比较复杂的逻辑。如果在lambda表达式中出现过于复杂的逻辑,会对程序的可读性造成非常大的影响。如果在lambda表达式中需要处理的逻辑比较复杂,一般会单独的写一个方法。在lambda表达式中直接引用这个方法即可。

    或者,在有些情况下,我们需要在lambda表达式中实现的逻辑,在另外一个地方已经写好了。此时我们就不需要再单独写一遍,只需要直接引用这个已经存在的方法即可。

    函数引用:引用一个已经存在的方法,使其替代lambda表达式完成接口的实现。

    3.1、静态方法的引用

    • 语法:

      • 类::静态方法
    • 注意事项:

      • 在引用的方法后面,不要添加小括号。
      • 引用的这个方法,参数(数量、类型) 和 返回值,必须要跟接口定义的一致。
    • 示例:

      /**
       * @Description  函数引用 ---> 静态方法的引用
       * @Author BF <xu.rongchang@foxmail.com>
       * @Version 1.0
       * @Date 2021/6/3
       */
      public class Syntax1 {
      
          private interface Calculator{
              int cacl(int a ,int b);
          }
          public static void main(String[] args) {
              Calculator calculator = Syntax1::cal;
              System.out.println(calculator.cacl(10,15));
          }
      
          public static int cal(int a ,int b){
              if (a>b){
                  return a - b;
              }else if (b>a){
                  return b - a;
              }
              return a + b ;
          }
      }
      

    3.2、非静态方法的引用

    • 语法:

      • 对象::非静态方法
    • 注意事项:

      • 在引用的方法后面,不要添加小括号。
      • 引用的这个方法,参数(数量、类型)和 返回值,必须要跟接口中定义的一致。
    • 示例:

      /**
       * @Description  函数引用 ---> 非静态方法的引用
       * @Author BF <xu.rongchang@foxmail.com>
       * @Version 1.0
       * @Date 2021/6/3
       */
      public class Syntax1 {
      
          private interface Calculator{
              int cacl(int a ,int b);
          }
          public static void main(String[] args) {
              Calculator calculator = new Syntax1()::cal;
              System.out.println(calculator.cacl(10,15));
          }
      
          public  int cal(int a ,int b){
              if (a>b){
                  return a - b;
              }else if (b>a){
                  return b - a;
              }
              return a + b ;
          }
      }
      

    3.3、构造方法的引用

    • 使用场景:

      • 如果某一个函数式接口中定义的方法,仅仅是为了得到一个类的对象。此时我们就可以使用构造方法的引用,简化这个方法的实现。
    • 语法:

      • 类名::new
    • 注意事项:

      • 可以通过接口中的方法的参数,区分引用不同的构造方法。
    • 示例:

      /**
       * @Description  函数引用 ---》 构造方法的引用
       * @Author BF <xu.rongchang@foxmail.com>
       * @Version 1.0
       * @Date 2021/6/3
       */
      public class Lambda1 {
      
          private static class Person{
              public Person() {
                  System.out.println("Person类的无参构造执行了.....");
              }
              public Person(String str) {
                  System.out.println("Person类的一个参数的构造方法执行了.....");
              }
              public Person(String str,int age) {
                  System.out.println("Person类的多个参数的构造方法执行了.....");
              }
          }
          @FunctionalInterface
          private interface GetPersonWithNoneParameter{
              Person get();
          }
          @FunctionalInterface
          private interface GetPersonWithSingleParameter{
              Person get(String str);
          }
          @FunctionalInterface
          private interface GetPersonWithMultipleParameter{
              Person get(String str,int age);
          }
      
          public static void main(String[] args) {
              // 1、通过 lambda 表达式,实现了 GetPersonWithNoneParameter 接口
              GetPersonWithNoneParameter person1 = Person::new;
              // 2、通过 lambda 表达式,实现了 GetPersonWithSingleParameter 接口
              GetPersonWithSingleParameter person2 = Person::new;
              // 3、通过 lambda 表达式,实现了 GetPersonWithMultipleParameter 接口
              GetPersonWithMultipleParameter person3 = Person::new;
      
              person1.get();
              person2.get("111");
              person3.get("123",123);
          }
      }
      

    3.4、对象方法的特殊引用

    如果在使用lambda表达式,实现某些接口的时候。lambda表达式中包含了某一个对象,此时方法体中,直接使用这个对象调用它的某一个方法就可以完成整体的逻辑。其它的参数,可以作为调用方法的参数。此时,可以对这种实现进行简化。

    /**
     * @Description 函数引用 ---》 对象方法的特殊引用(非静态方法)
     * @Author BF <xu.rongchang@foxmail.com>
     * @Version 1.0
     * @Date 2021/6/3
     */
    public class Lambda2 {
    
        private static class Person{
            private String name;
    
            public void setName(String name) {
                this.name = name;
            }
    
            public String getName() {
                return name;
            }
    
            public void show(){
    
            }
            public int showTest(int a ,int b){
                    return a + b;
            }
        }
    
        @FunctionalInterface
        private interface GetName{
            String getName01(Person person);
        }
    
        @FunctionalInterface
        private interface SetName{
            void setName02(Person person,String name);
        }
    
        @FunctionalInterface
        private interface VoidName{
            void voidName02(Person person);
        }
    
        @FunctionalInterface
        private interface AddShow{
            void addShow1(Person person,int a,int b);
        }
    
    
        public static void main(String[] args) {
    
            GetName getName1 = Person -> Person.getName();
            /**
             * lambda表达式中包含了某一个对象,此时方法体中,直接使用这个对象调用它的某一个方法就可以完成整体的逻辑。
             * 此时,可以对这种实现进行简化。
             * */
            GetName getName2 = Person::getName;
    
    
            SetName setName3 = (Person,name) -> Person.setName(name);
            /**
             * lambda表达式中包含了某一个对象,此时方法体中,直接使用这个对象调用它的某一个方法就可以完成整体的逻辑。
             * 其它的参数,可以作为调用方法的参数。
             * 此时,可以对这种实现进行简化。
             * */
            SetName setName4 = Person::setName;
    
    
    
            /**
             * 对于这个方法的实现逻辑,刚好是调用参数对象的某一个方法
            **/
    
            VoidName voidName5 = Person -> Person.show();
            VoidName voidName6 = Person::show;
    
    
            /**
             * 对于这个方法的实现逻辑,刚好是调用参数对象的某一个方法
             * 带参数了和上面差不多
             **/
            AddShow addShow7 = (Person,a,b) -> Person.showTest(a, b);
            AddShow addShow8 = Person::showTest;
    
        }
    }
    

    4 、Lambda表达式需要注意的地方

    /**
     * @Description  Lambda表达式需要注意的地方
     * @Author BF <xu.rongchang@foxmail.com>
     * @Version 1.0
     * @Date 2021/6/3
     */
    public class Lambda3 {
    
            //static int x = 100;
        public static void main(String[] args) {
            // 1、定义一个局部变量
            int x = 100;
            // 2、使用Lambda表达式实现接口
            AddTest addTest = () -> System.out.println("X的值为:"+x);
            // 3、修改变量的值
            int x = 50;
    
            /**
             * 发现 x 的值,无法被修改;
             * 原因在于 局部变量x传递进 Lambda表达式后形成包围(闭包)了。而且 x 的修饰符将增加为 final
             * 导致下一行代码无法进行修改。
             * 解决:我们将局部变量升级为全局变量 ,即可解决为题!
             * */
        }
    }
    
    @FunctionalInterface
    interface AddTest{
        void test();
    }
    
  • 相关阅读:
    Centos下安装Redis
    Web框架的本质
    DOM Event
    HTML DOM
    JavaScript运算符
    JavaScript基础
    开发中常用的插件与框架
    selector模块
    IO模型(阻塞、非阻塞、多路复用与异步)
    事件驱动模型
  • 原文地址:https://www.cnblogs.com/whitespaces/p/14847600.html
Copyright © 2011-2022 走看看