• Java基础(十二)lambda表达式


      1.引入lambda表达式的重要性

      lambda表达式是一个可传递的代码块,可以在以后执行一次或多次。

      在前面的回调部分,有一个例子是,ActionListener类实现了TimePrinter接口并在类中定义了一些方法作为到达时间后要发生的动作。然后将listener对象传递给Timer类的构造器中,启动构造器之后,一定的时间间隔后就可以执行ActionListener类中的动作方法。

    class TimePrinter implements ActionListener
    {  
       public void actionPerformed(ActionEvent event)
       {  
          System.out.println("At the tone, the time is " + new Date());
       }
    }

      还有一个例子是,如果要根据长度而不是默认的字典顺序对字符串排序,使用传统的接口的方法就是先定义一个实现了Comparator接口的类LengthComparator,然后实现其中的compare方法,然后创建一个LengthComparator的实例,最后将这个对象传递给Array.sort方法就可以实现按照字符串大小的顺序排列。

    class LengthComparator implement Comparator<String>
    {
        public int compare(String first, String second){
              return first.length() - second.length();
        }    
    }

    ...
    Arrays.sort(strings, new LengthComparator)

      这两个例子的共同点就是,都是将一个代码块传递到某个对象(一个是定时器,一个是sort方法),而这个代码将会在将来的某个时间被调用。sort方法调用compare方法也不是立即调用的,而是在数组排序完成前,才会一直调用compare方法,如果顺序不正确就会重新排列元素。

      如果不使用lambda表达式,在Java中不能直接传递代码段,必须创建一个对象,而这个对象的类需要一个方法能包含要传递的代码。

      2.lambda表达式语法

      上面的LengthComparator类中的compare中的方法用lambda表达式表示就是:

    (first, second) -> first.length() - second.length()

      而ActionListener类中的actionPerformed方法用lambda表达式表示就是:

    ActionListener listenter = event ->
             System.out.println("The time is " + new Date())

      这里可以看出lambda表达式的基本语法就是:(参数1类型  参数1, 参数2类型 参数2,...)  -> 表达式或代码块。

      例如:

      ①如果代码无法放在一个表达式中,则可以把这些代码放在大括号{}中。如果只在某些分支返回值,那么就不合法,例如没有下面的else代码,表达式就不合法。

    (String first, String second)  ->
        {
             if(first.length() < second.length()  return -1;
             else return 0;   
        }

      ②即使没有参数,也仍然要提供括号,就像没有参数的方法一样

    () -> {for(int i = 1; i < 10; i++) System.out.print(i)};

      ③lambda表达式不需要执行返回值类型,可以根据上下文推导得出。

      举例:

    package lambda;
    
    import java.util.*;
    
    import javax.swing.*;
    import javax.swing.Timer;
    
    /**
     * This program demonstrates the use of lambda expressions.
     * @version 1.0 2015-05-12
     * @author Cay Horstmann
     */
    public class LambdaTest
    {
       public static void main(String[] args)
       {
          String[] planets = new String[] { "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune" };
          System.out.println(Arrays.toString(planets)); // 打印:[Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune]
          Arrays.sort(planets);
          System.out.println(Arrays.toString(planets)); // 按字典顺序的默认排序:[Earth, Jupiter, Mars, Mercury, Neptune, Saturn, Uranus, Venus]
          System.out.println("Sorted by length:");
          Arrays.sort(planets, (first, second) -> first.length() - second.length());
          System.out.println(Arrays.toString(planets));// 按字符串长度排序:[Mars, Earth, Venus, Saturn, Uranus, Jupiter, Mercury, Neptune]
                
          Timer t = new Timer(1000, event -> System.out.println("The time is " + new Date()));
          t.start();     // 每个1秒,打印:The time is Wed Jul 18 20:09:40 GMT+08:00 2018...
       }
    }

      3.函数式接口

      像前面的例子中,当表达式需要一个接口类的对象,并且这个接口类只有一个方法时,就可以提供一个lambda表达式,这种接口称为函数式接口。

      还是以Array.sort方法来分析:

    Arrays.sort(planets, (first, second) -> first.length() - second.length());

      底层的实现过程是:Arrays.sort方法会接收实现了Comparator<string>的某个类的对象。在这个对象上调用compare方法会执行这个lambda表达式的体。这些对象和类的管理完全取决于具体实现,与使用传统的内联类相比,这样可能要高效得多。实际上,最好把lambda表达式看成是一个函数,而不是一个对象。

      4.方法引用

      如果已经有现成的方法可以完成想要传递到其他代码中的某个动作,可以使用方法引用。

      例如:在定时器中定时打印这个动作事件对象本身,使用传统的lambda表达式是:

    Timer t = new Timer(1000, event -> System.out.println(event));

      如果使用方法引用的话,可以写成这样:和上面的表达等价

    Timer t = new Timer(1000, System.out::println);

      总结来说,就是x -> System.out.println(x)等价于System.out::println,相当于把println方法传递了进去,所以叫方法引用。

      方法引用的四种类型:

    • object::instanceMethod(obj::show等价于() -> obj.show())
    • Class::staticMethod(x -> System.out.println(x)等价于System.out::println;Math::pow等价于(x,y->Math.pow(x,y))
    • Class::instanceMethod(String::compareToIgnoreCase等价于(x,y)->x.compareToIgnoreCase(y))
    • this和super(this::equals等价于x -> this.equals(x);Timer t = new Timer(1000, super::greet

      5.构造器引用
      构造器引用于方法名引用类似,只不过方法名是new而已。

      例如,

      Person::new是Person构造器的一个引用,如果有多个构造器,将会自动从上下文推导出一个构造器。

      int[]::new是等价于lambda表达式x -> new int[x]

      

      6.变量作用域

      lambda表达式可以捕获外围作用域中变量的值,但是有一些限制:

    • 只能引用值不会改变的变量。
    • 只能引用不会在外部改变的变量。
    • 变量必须是最终变量,即变量初始化后不会再为它赋值
    • 不能声明一个与外部局部变量同名的变量
    • 不能有两个同名的局部变量
    • lambda表达式中this关键字是指lambda表达式所在的类对象。

      

      7.应用lambda表达式的Comparator接口

      Comparator接口中包含很多方便的静态方法来创建比较器,这些方法可以用lambda表达式表达或引用。

      (1)假设有一个Person对象数组,要按名字对这些对象进行排序:

    Arrays.sort(people, Comparator.comparing(Person::getName));

      (2)比较名字的另一种表达,先比较姓,如果相等再比较名:

    Arrays.sort(people, Comparator.comparing(Person::getLastName).thenComparing(Person::getFirstName));

      (3)根据人名的长度比较排序:

    Arrays.sort(people, Comparator.comparing(Person::getName), (s,t)->Integer.compare(s.length(), t.length());

      (4)遇到中文名字null时不会抛出异常,并且按照自然顺序比较(按自然顺序逆序reverseOrder()):

    Arrays.sort(people, Comparator.comparing(Person::getMiddleName), Comparator.nullFirst(naturalOrder());
  • 相关阅读:
    mongoDB知识总结
    利用js来实现一些常用的算法
    onclick和onblur的冲突问题
    GitHub 基本操作流程
    Spark UDAF实现举例 -- average pooling
    信息论基本概念
    【神经网络和深度学习】笔记
    【神经网络和深度学习】笔记
    【神经网络和深度学习】笔记
    【神经网络和深度学习】笔记
  • 原文地址:https://www.cnblogs.com/BigJunOba/p/9329545.html
Copyright © 2020-2023  润新知