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());

Java基础(十二)lambda表达式的更多相关文章

  1. Java基础十二--多态是成员的特点

    Java基础十二--多态是成员的特点 一.特点 1,成员变量. 编译和运行都参考等号的左边. 覆盖只发生在函数上,和变量没关系. Fu f = new Zi();System.out.println( ...

  2. Java基础教程:Lambda表达式

    Java基础教程:Lambda表达式 本文部分内容引用自OneAPM:http://blog.oneapm.com/apm-tech/226.html 引入Lambda Java 是一流的面向对象语言 ...

  3. Java基础教程(23)--lambda表达式

    一.初识lambda表达式 1.定义   lambda表达式是一个可传递的代码块,或者更确切地说,可以把lambda表达式理解为简洁地表示可传递的匿名方法的一种方式.它没有名称,但它有参数列表.函数主 ...

  4. Util应用程序框架公共操作类(十二):Lambda表达式公共操作类(三)

    今天在开发一个简单查询时,发现我的Lambda操作类的GetValue方法无法正确获取枚举类型值,以至查询结果错误. 我增加了几个单元测试来捕获错误,代码如下. /// <summary> ...

  5. Java基础(十二)--clone()方法

    Clone在Java中就是用来复制对象,通过分配一个和源对象相同大小的内存空间,然后创建一个新的对象,那么他和=的区别在哪? 通过=实现对象拷贝: @Data @NoArgsConstructor @ ...

  6. java基础(十二 )-----Java泛型详解

    本文对java的泛型的概念和使用做了详尽的介绍. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用. 什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”.一提到 ...

  7. Java基础(十二)之包和权限访问

    软件包 软件包解决了两个类名字一样的问题.软件包就是一个"文件夹". 包名的命名规范:1.要求所有字母都小写:2.包名一般情况下,是你的域名倒过来写.比如baidu.com,pac ...

  8. java基础十二[集合与泛型](阅读Head First Java记录)

    集合 List 知道索引顺序的集合,ArrayList.LinkedList.Vector三个子类实现了List接口   ArrayList ArrayList没有排序方法,可以用Collection ...

  9. Java 基础(二)| 使用 lambad 表达式的正确姿势

    前言 为跳槽面试做准备,今天开始进入 Java 基础的复习.希望基础不好的同学看完这篇文章,能掌握 lambda 表达式,而基础好的同学权当复习,希望看完这篇文章能够起一点你的青涩记忆. 一.什么是 ...

  10. Java实习生常规技术面试题每日十题Java基础(二)

    目录 1. JAVA 的反射机制的原理. 2.静态嵌套类(Static Nested Class)和内部类(Inner Class)的不同? 3.如何将String类型转化成Number类型. 4.什 ...

随机推荐

  1. 不知道如何实现服务的动态发现?快来看看 Dubbo 是如何做到的

    上篇文章如果有人问你 Dubbo 中注册中心工作原理,就把这篇文章给他大致了解了注册中心作用以及 Dubbo Registry 模块源码,这篇文章将深入 Dubbo ZooKeeper 模块,去了解如 ...

  2. springboot启动后自动退出

    有时新建的springboot启动后自动退出运行,如图所示: 此种情况大都数是因为pom文件加入了tomcat的依赖,与springboot内嵌的tomcat冲突导致,所以只需将pom文件中的tomc ...

  3. Redis-->windows上的安装教程

    Windows下安装Redis服务 说明:本文拷贝自http://www.cnblogs.com/jaign/articles/7920588.html Redis是有名的NoSql数据库,一般Lin ...

  4. C++ProtoBuf的安装与使用

    目录 安装(Ubuntu 16.04) 简介 proto2 proto3 用法 proto3 输出结果 总结 @(目录) 安装(Ubuntu 16.04) sudo apt-get install a ...

  5. 在 Vue-cli 创建的项目中引入 Element-UI

    Element-UI 是饿了么前端团队退出了一套基于 vue.js 开发的 UI 组件库,在与 Vue-cli 创建的项目结合时,需要做以下配置: 1. 安装 loader 模块 cnpm insta ...

  6. linux shell 统计当前目录下的文件个数

    shell 统计当前目录下文件个数,使用管道组合命令: ls -1 | wc -l 解释: ls -1 表示一行一个列出文件名. wc -l 表示打印统计的行数. 两个命令通过管道连在一起表示打印列出 ...

  7. Springboot之初入江湖

    Hello,各位小伙伴大家好,我是小栈君. 今天的分享主题是关于Springboot主题分享,其实在写这个系列主题之前有想过一些关于分享技术的顺序问题,因为我在创建"IT干货栈"这 ...

  8. Xshell、Xftp 5、6 解决“要继续使用此程序,您必须应用最新的更新或使用新版本”

    今天打开Xshell.Xftp,突然弹出“要继续使用此程序,您必须应用最新的更新或使用新版本”. 后来经过一番搜索发现,XShell配置文件中写入了强制升级时间,这个版本是2017年12月27日发布的 ...

  9. eclipse中Tomcat version 9.0 only supports J2EE 1.2, 1.3, 1.4, and Java EE 5, 6, 7, and 8 Web modules

    eclipse中导入了一个别人的项目,运行时提示没有可以使用的服务器,如下: 查看了下项目属性设置中的服务器,还是提示没有可用服务器: 尝试对部署在已有服务器下的项目Add and Remove... ...

  10. hibernate 搭建框架

    需要用的包 Hibernate的日志记录: * Hibernate日志记录使用了一个slf4j: * SLF4J,即简单日志门面(Simple Logging Facade for Java),不是具 ...