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

    目录

    第一章:函数式编程介绍

    在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。相对而言,面向对象过

    分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以

    什么形式做。

    面向对象的思想: 做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情。

    函数式编程思想: 只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程 。

    第二章: 冗余的Runnable代码

    • 传统写法:

      // 当需要启动一个线程去完成任务时,通常会通过 java.lang.Runnable 接口来定义任务内容,并使用 java.lang.Thread 类来启动该线程。代码如下:
      public class Demo01Runnable { 
          public static void main(String[] args) { 
              // 匿名内部类 
              Runnable task = new Runnable() { 
                  @Override 
                  public void run() {
                    // 覆盖重写抽象方法 
                    System.out.println("多线程任务执行!");
                  } 
              };
              new Thread(task).start(); // 启动线程 
          }
      }
      // 本着“一切皆对象”的思想,这种做法是无可厚非的:首先创建一个 Runnable 接口的匿名内部类对象来指定任务内 容,再将其交给一个线程来启动。
      
    • 代码分析:

      1. Thread 类需要 Runnable 接口作为参数,其中的抽象 run 方法是用来指定线程任务内容的核心;
      2. 为了指定 run 的方法体,不得不需要 Runnable 接口的实现类;
      3. 为了省去定义一个 RunnableImpl 实现类的麻烦,不得不使用匿名内部类;
      4. 必须覆盖重写抽象 run 方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;
      5. 而实际上,似乎只有方法体才是关键所在。

    第三章: 编程思想转换

    • 做什么,而不是怎么做

      ​ 我们真的希望创建一个匿名内部类对象吗?不。我们只是为了做这件事情而不得不创建一个对象。我们真正希望做的事情是:将 run 方法体内的代码传递给 Thread 类知晓。

      ​ 传递一段代码——这才是我们真正的目的。而创建对象只是受限于面向对象语法而不得不采取的一种手段方式。

      ​ 那有没有更加简单的办法?如果我们将关注点从“怎么做”回归到“做什么”的本质上,就会发现只要能够更好地达 到目的,过程与形式其实并不重要

    • 生活举例

      ​ 当我们需要从北京到上海时,可以选择高铁、汽车、骑行或是徒步。我们的真正目的是到达上海,而如何才能到达上海的形式并不重要,所以我们一直在探索有没有比高铁更好的方式——搭乘飞机。

      ​ 而现在这种飞机(甚至是飞船)已经诞生:**2014年3月Oracle所发布的Java 8(JDK 1.8)中,加入了Lambda表达式的重量级新特性,为我们打开了新世界的大门。 **

    第四章:体验Lambda的更优写法

    ​ 借助Java 8的全新语法,上述 Runnable 接口的匿名内部类写法可以通过更简单的Lambda表达式达到等效:

    public class Test {
      public static void main(String[] args) {
        new Thread(()->System.out.println("多线程任务执行!")).start();
      }
    }
    

    ​ 这段代码和刚才的执行效果是完全一样的,可以在1.8或更高的编译级别下通过。从代码的语义中可以看出:我们启动了一个线程,而线程任务的内容以一种更加简洁的形式被指定。

    ​ 不再有“不得不创建接口对象”的束缚,不再有“抽象方法覆盖重写”的负担,就是这么简单!

    第五章:Lambda标准格式

    • Lambda省去面向对象的条条框框,格式由3个部分组成:
      1. 一些参数
      2. 一个箭头
      3. 一段代码
    • 标准格式: (参数类型 参数名称) ‐> { 代码语句 }
    • 格式说明:
      1. 小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。
      2. -> 是新引入的语法格式,代表指向动作。
      3. 大括号内的语法与传统方法体要求基本一致。

    第六章:无参数无返回值的代码

    • 题目

      • 给定一个厨子 Cook 接口,内含唯一的抽象方法 makeFood ,且无参数、无返回值。如下:

      • 代码:

        public interface Cook { void makeFood(); }
        // 在下面的代码中,请使用Lambda的标准格式调用 invokeCook 方法,打印输出“吃饭啦!”字样:
        public class Demo05InvokeCook { 
            public static void main(String[] args) { 
                // TODO 请在此使用Lambda【标准格式】调用invokeCook方法 
            }
            private static void invokeCook(Cook cook) { 
                cook.makeFood(); 
            } 
        }
        
    • 解答:

      invokeCook(() ‐> { System.out.println("吃饭啦!"); });
      

    第七章:有参数有返回值的代码

    • 题目:使用数组存储多个Person对象对数组中的Person对象使用Arrays的sort方法通过年龄进行升序排序

      下面举例演示 java.util.Comparator<T> 接口的使用场景代码,其中的抽象方法定义为:

      • public abstract int compare(T o1, T o2);

      当需要对一个对象数组进行排序时, Arrays.sort 方法需要一个 Comparator 接口实例来指定排序的规则。假设有一个 Person 类,含有 String name 和 int age 两个成员变量:

      public class Person { 
          private String name; 
          private int age; 
          // 省略构造器、toString方法与Getter Setter 
      }
      
    • 传统方式:

      public class Test {
        public static void main(String[] args) {
          // 本来年龄乱序的对象数组
          Person[] array = {
                  new Person("古力娜扎", 19),
                  new Person("迪丽热巴", 18),
                  new Person("马尔扎哈", 20)
          };
          // 排序
          Arrays.sort(array, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
              return o1.getAge() - o2.getAge();
            }
          });
          // 打印
          for (int i = 0; i < array.length; i++) {
            System.out.println(array[i].getName() + "---" + array[i].getAge());
          }
        }
      }
      
      // 这种做法在面向对象的思想中,似乎也是“理所当然”的。其中 Comparator 接口的实例(使用了匿名内部类)代表 了“按照年龄从小到大”的排序规则。
      
    • 代码分析:下面我们来搞清楚上述代码真正要做什么事情。

      1. 为了排序, Arrays.sort 方法需要排序规则,即 Comparator 接口的实例,抽象方法 compare 是关键;
      2. 为了指定 compare 的方法体,不得不需要 Comparator 接口的实现类;
      3. 为了省去定义一个 ComparatorImpl 实现类的麻烦,不得不使用匿名内部类;
      4. 必须覆盖重写抽象 compare 方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;
      5. 实际上,只有参数和方法体才是关键。
    • Lambda方式:

      public class Test {
        public static void main(String[] args) {
          // 本来年龄乱序的对象数组
          Person[] array = {
                  new Person("古力娜扎", 19),
                  new Person("迪丽热巴", 18),
                  new Person("马尔扎哈", 20)
          };
          // 排序
          Arrays.sort(array,(Person o1, Person o2) ->{
            return o1.getAge()-o2.getAge();
          });
          // 打印
          for (int i = 0; i < array.length; i++) {
            System.out.println(array[i].getName() + "---" + array[i].getAge());
          }
        }
      }
      

    第八章:Lambda省略格式

    可推导即可省略。

    • 省略规则:

      1. 小括号内参数的类型可以省略;
      2. 如果小括号内有且仅有一个参,则小括号可以省略;
      3. 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
    • 代码:

      // 【练习2代码:】
      // 省略前
          Arrays.sort(array,(Person o1, Person o2) ->{
            return o1.getAge()-o2.getAge();
          });
      // 省略后
          Arrays.sort(array,(o1, o2) ->return o1.getAge()-o2.getAge());
      

    第九章:Lambda使用前提

    Lambda的语法非常简洁,完全没有面向对象复杂的束缚。但是使用时有几个问题需要特别注意:

    1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。

      无论是JDK内置的 Runnable 、 Comparator 接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。

    2. 使用Lambda必须具有上下文推断。

      也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

    备注:有且仅有一个抽象方法的接口,称为“函数式接口”

  • 相关阅读:
    NYOJ 625 笨蛋的难题(二)
    NYOJ 102 次方求模
    ZJU Least Common Multiple
    ZJUOJ 1073 Round and Round We Go
    NYOJ 709 异形卵
    HDU 1279 验证角谷猜想
    BNUOJ 1015 信息战(一)——加密程序
    HDU 1202 The calculation of GPA
    "蓝桥杯“基础练习:字母图形
    "蓝桥杯“基础练习:数列特征
  • 原文地址:https://www.cnblogs.com/lpl666/p/12008925.html
Copyright © 2011-2022 走看看