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());
Java基础(十二)lambda表达式的更多相关文章
- Java基础十二--多态是成员的特点
Java基础十二--多态是成员的特点 一.特点 1,成员变量. 编译和运行都参考等号的左边. 覆盖只发生在函数上,和变量没关系. Fu f = new Zi();System.out.println( ...
- Java基础教程:Lambda表达式
Java基础教程:Lambda表达式 本文部分内容引用自OneAPM:http://blog.oneapm.com/apm-tech/226.html 引入Lambda Java 是一流的面向对象语言 ...
- Java基础教程(23)--lambda表达式
一.初识lambda表达式 1.定义 lambda表达式是一个可传递的代码块,或者更确切地说,可以把lambda表达式理解为简洁地表示可传递的匿名方法的一种方式.它没有名称,但它有参数列表.函数主 ...
- Util应用程序框架公共操作类(十二):Lambda表达式公共操作类(三)
今天在开发一个简单查询时,发现我的Lambda操作类的GetValue方法无法正确获取枚举类型值,以至查询结果错误. 我增加了几个单元测试来捕获错误,代码如下. /// <summary> ...
- Java基础(十二)--clone()方法
Clone在Java中就是用来复制对象,通过分配一个和源对象相同大小的内存空间,然后创建一个新的对象,那么他和=的区别在哪? 通过=实现对象拷贝: @Data @NoArgsConstructor @ ...
- java基础(十二 )-----Java泛型详解
本文对java的泛型的概念和使用做了详尽的介绍. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用. 什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”.一提到 ...
- Java基础(十二)之包和权限访问
软件包 软件包解决了两个类名字一样的问题.软件包就是一个"文件夹". 包名的命名规范:1.要求所有字母都小写:2.包名一般情况下,是你的域名倒过来写.比如baidu.com,pac ...
- java基础十二[集合与泛型](阅读Head First Java记录)
集合 List 知道索引顺序的集合,ArrayList.LinkedList.Vector三个子类实现了List接口 ArrayList ArrayList没有排序方法,可以用Collection ...
- Java 基础(二)| 使用 lambad 表达式的正确姿势
前言 为跳槽面试做准备,今天开始进入 Java 基础的复习.希望基础不好的同学看完这篇文章,能掌握 lambda 表达式,而基础好的同学权当复习,希望看完这篇文章能够起一点你的青涩记忆. 一.什么是 ...
- Java实习生常规技术面试题每日十题Java基础(二)
目录 1. JAVA 的反射机制的原理. 2.静态嵌套类(Static Nested Class)和内部类(Inner Class)的不同? 3.如何将String类型转化成Number类型. 4.什 ...
随机推荐
- 不知道如何实现服务的动态发现?快来看看 Dubbo 是如何做到的
上篇文章如果有人问你 Dubbo 中注册中心工作原理,就把这篇文章给他大致了解了注册中心作用以及 Dubbo Registry 模块源码,这篇文章将深入 Dubbo ZooKeeper 模块,去了解如 ...
- springboot启动后自动退出
有时新建的springboot启动后自动退出运行,如图所示: 此种情况大都数是因为pom文件加入了tomcat的依赖,与springboot内嵌的tomcat冲突导致,所以只需将pom文件中的tomc ...
- Redis-->windows上的安装教程
Windows下安装Redis服务 说明:本文拷贝自http://www.cnblogs.com/jaign/articles/7920588.html Redis是有名的NoSql数据库,一般Lin ...
- C++ProtoBuf的安装与使用
目录 安装(Ubuntu 16.04) 简介 proto2 proto3 用法 proto3 输出结果 总结 @(目录) 安装(Ubuntu 16.04) sudo apt-get install a ...
- 在 Vue-cli 创建的项目中引入 Element-UI
Element-UI 是饿了么前端团队退出了一套基于 vue.js 开发的 UI 组件库,在与 Vue-cli 创建的项目结合时,需要做以下配置: 1. 安装 loader 模块 cnpm insta ...
- linux shell 统计当前目录下的文件个数
shell 统计当前目录下文件个数,使用管道组合命令: ls -1 | wc -l 解释: ls -1 表示一行一个列出文件名. wc -l 表示打印统计的行数. 两个命令通过管道连在一起表示打印列出 ...
- Springboot之初入江湖
Hello,各位小伙伴大家好,我是小栈君. 今天的分享主题是关于Springboot主题分享,其实在写这个系列主题之前有想过一些关于分享技术的顺序问题,因为我在创建"IT干货栈"这 ...
- Xshell、Xftp 5、6 解决“要继续使用此程序,您必须应用最新的更新或使用新版本”
今天打开Xshell.Xftp,突然弹出“要继续使用此程序,您必须应用最新的更新或使用新版本”. 后来经过一番搜索发现,XShell配置文件中写入了强制升级时间,这个版本是2017年12月27日发布的 ...
- 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... ...
- hibernate 搭建框架
需要用的包 Hibernate的日志记录: * Hibernate日志记录使用了一个slf4j: * SLF4J,即简单日志门面(Simple Logging Facade for Java),不是具 ...