Java开发笔记(六十一)Lambda表达式
前面介绍了匿名内部类的简单用法,通过在sort方法中运用匿名内部类,不但能够简化代码数量,还能保持业务代码的连续性。只是匿名内部类的结构仍显啰嗦,虽然它省去了内部类的名称,但是花括号里面的方法定义代码一字不落,依然生生占据了好几行代码。比如下面排序方法的调用代码例子:
Integer[] intArray = { 89, 3, 67, 12, 45 };
// 匿名内部类无需专门定义形态完整的类,只需指明新创建的实例从哪个接口扩展而来
Arrays.sort(intArray, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o2, o1); // 倒过来的参数顺序变成了降序
}
});
尽管这种匿名内部类的代码有点别扭,然而在早期的Java编程中也只能如此了,毕竟还得按照面向对象的代码规矩来,否则缺胳膊断腿的匿名内部类,编译器怎知它是什么玩意?直到Java8推出了Lambda表达式,才迎来了匿名内部类代码优化的曙光。
Lambda表达式其实是一个匿名方法,所谓匿名方法指的是:它是个没有名字的方法,但方法体的内部代码是完整的。可是常规的方法调用都必须指定方法名称,假如匿名方法不存在方法名称,那么别的地方要怎样才能调用它呢?为了保证编译器能够识别匿名方法的真身,Java对它的调用时机规定了以下限制条件:
1、调用匿名方法的地方,本身必须知晓该位置的参数类型。举个例子,Math库的对数函数log,根据方法定义可知,它的输入参数是双精度类型,则程序员书写“Math.log(1)”的时候,虽然这个1看不出数值类型,编译器也会自动将它转换为双精度数。
2、参数类型必须是某个接口,并且该接口仅声明了一个抽象方法。由于Java体系里的方法参数要么是基本变量类型如int、double,要么是某个类或某个接口,就是不支持把方法作为参数类型,因此需要借助接口把某个方法单独包装一下,这样每当给这个接口创建匿名内部类的时候,编译器便知道接下来只能且必定调用该接口的唯一方法。
根据以上的两个行规,对比排序方法sort可知该方法满足第一项条件,同时排序比较器Comparator也满足第二项条件,于是调用sort方法出现的匿名内部类完全支持改写为Lambda表达式。一方面,因为拥有两个参数的sort方法早已声明第二个参数是Comparator类型,所以匿名内部类当中的该接口名称允许略去;另一方面,因为比较器Comparator只有唯一的抽象方法compare,所以匿名内部类里面的方法名称也允许略去。如此一来,既省略接口名又省略方法名的Lambda排序代码示例如下:
// Lambda表达式第一招。去掉了new、接口名称、方法名称
Arrays.sort(intArray, (Integer o1, Integer o2) -> {
return Integer.compare(o2, o1); // 按照降序排列
});
仔细观察上述的Lambda表达式,发现compare方法的参数列表与方法体之间多了箭头标志“->”,这正是Lambda表达式的特征标记,箭头左边为匿名方法的参数列表,箭头右边为匿名方法的方法体。注意到参数列表中仍然保留了每个参数的类型名称,其实依据compare方法的定义,对于整型数组而言,此处的两个输入参数一定是Integer类型,故而参数列表里的类型名称可以统统去掉。这样进一步简化后的Lambda表达式变成了下面代码:
// Lambda表达式第二招。去掉了输入参数的变量类型
Arrays.sort(intArray, (o1, o2) -> {
return Integer.compare(o2, o1); // 按照降序排列
});
尽管上面的Lambda表达式已经足够简洁了,但对于这种内部只有一行代码的方法体来说,还能用点劲继续压缩代码。首先,只有一行代码的话,包裹方法体的花括号赶紧去掉;其次,compare方法需要一个整型返回值,刚好“Integer.compare(o2, o1)”返回的正是整型数,因而这行代码前面的return也可去掉,顺便把末尾的分号一块扔了。于是经过三次精简的Lambda排序代码如下所示:
// Lambda表达式第三招。去掉了方法体的花括号,以及方法返回的return和分号
Arrays.sort(intArray, (o1, o2) -> Integer.compare(o2, o1));
这下终于把Lambda表达式压缩到了极致,连同sort方法在内都只有短短一行,比起匿名内部类的实现代码又前进了一大步。
再来一个字符串数组的排序练练手,有利于加深大家对Lambda表达式的理解。在上一篇文章中,对字符串数组按照长度排序的功能,通过匿名内部类的实现代码是下面这样的:
// 通过匿名内部类对字符串数组按照字符串长度进行排序
private static void sortStrArrayByLength() {
String[] strArray = { "说曹操曹操就到", "东道主", "风马牛不相及", "亡羊补牢", "无巧不成书",
"冰冻三尺非一日之寒", "同窗", "青出于蓝而胜于蓝" };
// 字符串数组的默认排序方式为根据首字母的拼写顺序,
// 下面的匿名内部类把排序方式改成了按照字符串长度进行排序
Arrays.sort(strArray, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// 比较前后两个数组元素的字符串长度大小
return o1.length() < o2.length() ? -1 : 1;
}
});
String desc = "字符串数组比较字符串长度的升序结果为:";
for (String item : strArray) {
desc = desc + item + ", ";
}
System.out.println(desc);
}
现在把排序器的匿名内部类代码改写为匿名方法,则精兵简政之后的Lambda表达式缩短到了如下一行代码:
// 下面的Lambda表达式把排序方式改成了按照字符串长度进行排序
Arrays.sort(strArray, (o1, o2) -> o1.length() < o2.length() ? -1 : 1);
别看Lambda代码如此精炼,该做什么编译器一个都没落下。运行包含Lambda表达式的测试代码,输出的日志结果明明白白,可见字符串数组果然按照升序排列了。
字符串数组比较字符串长度的升序结果为:同窗, 东道主, 亡羊补牢, 无巧不成书, 风马牛不相及, 说曹操曹操就到, 青出于蓝而胜于蓝, 冰冻三尺非一日之寒,
更多Java技术文章参见《Java开发笔记(序)章节目录》
Java开发笔记(六十一)Lambda表达式的更多相关文章
- Java开发笔记(十一)常见的数学函数
前面介绍了Java编程的四则运算,虽然提供了基础的加减乘除符号,但是数学上还有其它运算符号,包括四舍五入用到的约等号≍.求绝对值的“| |”.开平方的“√ ̄”,这些运算符形态各异,而且并非ASCII码 ...
- Java开发笔记(序)章节目录
现将本博客的Java学习文章整理成以下笔记目录,方便查阅. 第一章 初识JavaJava开发笔记(一)第一个Java程序Java开发笔记(二)Java工程的帝国区划Java开发笔记(三)Java帝国的 ...
- 【Java学习笔记之三十一】详解Java8 lambda表达式
Java 8 发布日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Java 8之前 ...
- Java开发笔记(八十一)如何使用系统自带的注解
之前介绍继承的时候,提到对于子类而言,父类的普通方法可以重写也可以不重写,但是父类的抽象方法是必须重写的,如果不重写,编译器就直接在子类名称那里显示红叉报错.例如,以前演示抽象类用法之时,曾经把Chi ...
- Java开发笔记(六十二)如何定义函数式接口
前面介绍了Lambda表达式的用法,从实践中发现它确实极大地方便了开发者,然而不管是匿名内部类还是Lambda表达式,所举的例子都离不开各类数组的排序方法,倘使Lambda表达式仅能用于sort方法, ...
- Java开发笔记(六十三)双冒号标记的方法引用
前面介绍了如何自己定义函数式接口,本文接续函数式接口的实现原理,阐述它在数组处理中的实际应用.数组工具Arrays提供了sort方法用于数组元素排序,可是并未提供更丰富的数组加工操作,比如从某个字符串 ...
- Java开发笔记(六十四)静态方法引用和实例方法引用
前面介绍了方法引用的概念及其业务场景,虽然在所列举的案例之中方法引用确实好用,但是显而易见这些案例的适用场合非常狭窄,因为被引用的方法必须属于外层匿名方法(即Lambda表达式)的数据类型,像isEm ...
- Java开发笔记(六十五)集合:HashSet和TreeSet
对于相同类型的一组数据,虽然Java已经提供了数组加以表达,但是数组的结构实在太简单了,第一它无法直接添加新元素,第二它只能按照线性排列,故而数组用于基本的操作倒还凑合,若要用于复杂的处理就无法胜任了 ...
- Java开发笔记(六十六)映射:HashMap和TreeMap
前面介绍了两种集合的用法,它们的共性为每个元素都是唯一的,区别在于一个无序一个有序.虽说往集合里面保存数据还算容易,但要从集合中取出数据就没那么方便了,因为集合居然不提供get方法,没有get方法怎么 ...
随机推荐
- Python练手例子(10)
55.学习使用按位取反~. 程序分析:~0=1; ~1=0; (1)先使a右移4位. (2)设置一个低4位全为1,其余全为0的数.可用~(~0<<4) (3)将上面二者进行&运算. ...
- FTP出现PORT模式成功, 请更新你的站点配置文件
最近用FTP连接站点,经常出现连接不上或者连接失败,提示以PASV模式连接失败,正在使用PORT模式连接,最后才能连接成功,连接时间也是相当长,又慢又不稳定. 工具/原料 FlashFXP等F ...
- [.net 面向对象程序设计深入](36)Redis——基础
[.net 面向对象程序设计深入](36)Redis——基础 很长一段时间没更新博客了,坚持做一件事,真不是件容易的事,后面我会继续尽可能的花时间更新完这个系列文章. 因这个系列的文章涉及的范围太大了 ...
- React Native调试实用技巧,React Native开发者必会的调试技巧
在做React Native开发时,少不了的需要对React Native程序进行调试.调试程序是每一位开发者的基本功,高效的调试不仅能提高开发效率,也能降低Bug率.本文将向大家分享React Na ...
- [Swift]LeetCode591. 标签验证器 | Tag Validator
Given a string representing a code snippet, you need to implement a tag validator to parse the code ...
- python 抓取糗事百科糗图
1 首先看下要抓取的页面 这是糗事百科里面的糗图页面,每一页里面有很多的图片,我们要做的就是把这些图片抓取下来. 2 分析网页源代码 发现源代码里面的每张图是这样储存的,所以决定使用正则匹配出图片的u ...
- 被BAT疯抢的工程师,都是怎么拿到50万年薪Offer的?
许多工程师换工作常会出现这种情况:工作能力和潜力都不错,却在面试时无法充分地表现自己,铩羽而归.据拉勾数据调研显示,约有80%的工程师简历通不过初筛,进入终面的不到5% . 技术面试到底应该如何准备? ...
- qt之图像处理
毕业2年了,一直使用的qt做桌面程序,很少接触图像算法类的东西,最近由于项目的原因,不得不了解下图像处理,不过也是一些简单的图像处理,仅此作为记录,并希望能帮助初学qt图像处理的朋友. 首先我推荐一篇 ...
- [Leetcode]674. Longest Continuous Increasing Subsequence
Given an unsorted array of integers, find the length of longest continuous increasing subsequence. E ...
- shell之最常用的服务脚本
任务需求:以最简单的方式管理 /usr/local/php7/sbin/php-fpm -c /usr/local/php7/etc/php.ini 这条命令 包括启动停止重启 使用技术:shell脚 ...