java se基础巩固实例,Java SE基础巩固(十五):lambda表达式
1 概述
Java8据说是Java诞生以来最大的一次演进,说实话,对我个人来说没有什么特别大的感受,因为我学Java也就最近一两年的事,Java8在2014年3月18日发布,新增的特性确实非常惊艳,在语言特性层面上新增了lambda,Optional,默认方法,Stream API等,在虚拟机层面上新增了G1收集器(不过在Java9之后才改为默认的垃圾收集器)......
我个人认为Java8和语言相关的几个最重要的特性是如下几个:
lambda表达式和方法引用(其实是lambda表达式的一种特例)
Stream API
接口的默认方法
Optinal
CompletableFuture
本系列文章的后面几篇文章会围绕这几个主题来展开,今天就先上个开胃菜,lambda表达式!
2 什么是lambda表达式
lambda表达式也叫做匿名函数,其基于著名的λ演算得名,关于λ演算,推荐大家去找找关于“丘奇数”相关的资料。Java一直被人诟病的一点就是“啰嗦”,通常为了实现一个小功能,就不得不编写大量的代码,而用其他的语言例如Python等,也许寥寥几行代码就解决了,但支持lambda表达式之后,这一情况得到了大大的改善,现在只要使用得当,可以大大缩减代码里,使代码的目的更加清晰,易读,纯粹。
在Java中,很多时候在使用一些API的时候,必须要给出一些接口的实现,但因为该实现其实也就用一次,专门去创建一个新的实现类并不划算,所以一般大多数人采取的措施应该是创建一个匿名实现类,比较典型就是Collections.sort(List list, Comparator super T> c)方法,该方法接受一个Comparator类型的参数,Comparator是一个接口,表示“比较器”,如果要使用该方法对集合元素进行排序,就必须提供一个Comparator接口的实现,否则无法通过编译。如下所示:
Collections.sort(numbers, new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
其实这个实现类的核心只有一行,即return o1.compareTo(o2);但我们却不得不编写其他“啰嗦”的代码,如果使用lambda表达式,会是怎么个样子呢?
Collections.sort(numbers, (n1, n2) -> n1.compareTo(n2));
没错,就是那么简单粗暴,就是一行核心代码。其他的比如方法签名啥的统统可以省略了,不仅简洁,而且语义也更加清晰,读起来就好像是说:“sort方法,帮我吧numbers这个序列排个序,排序规则就按照n1.compareTo(n2)的返回值来决定”。现在,是不是感觉,写代码就像在和计算机对话一样简单?但(n1, n2) -> n1.compareTo(n2)这玩意是个什么鬼?还带个箭头?不用着急,下面马上介绍lambda表达式的语法。
2.1 lambda表达式的语法
第一部分是lambda的参数列表,因为Comparator.compare()方法接受两个参数,所以这里给出两个参数n1和n2,可以省略具体的类型,Java编译器会自动推断。
第二部分是箭头,没什么特殊的地方,只是Java语言觉得使用这个,各个语言的实现也不太一样,例如Python是:号,简单理解的就当是把参数列表和函数主体分开的东西吧。
第三部分就是函数主体,也就是真正执行逻辑的地方。
如果函数主体仅仅包含一行代码,可以省略花括号{}和return关键字(如果有的话)。对于我们的例子,可以改写成这样:
Collections.sort(numbers, (n1, n2) -> {return n1.compareTo(n2);});
注意分号!因为此时return n1.compareTo(n2);就是一条普通的Java语句了,必须遵守Java的语法规则。好了,尽管我们现在明白了lambda语句的语法规则,但还有一个关键的问题,就是为什么要这样写,换句话说,为什么要有俩参数,这return又是几个意思?还有到底哪里才可以使用lambda表达式?说到这,就不得不说一下和lambda息息相关的东西了:函数式接口。
3 函数式接口
函数式接口是这样的:只有一个抽象方法的接口就是函数式接口。为什么要特别强调抽象方法呢?Java接口里声明的方法不都是抽象方法吗?在Java8之前,这么说确实没有任何问题,但Java8新增了接口的默认方法,可以在接口里给出方法的具体实现,这里先不多说,后面的文章会详细讨论这个东西。
lambda表达式仅可以用在函数式接口上,我们在上面遇到的Comparator就是一个函数式接口,他只有一个抽象方法:compare(),其方法签名是这样的:
int compare(T o1, T o2);
现在来看看 (n1, n2) -> n1.compareTo(n2)这个表达式,是不是发现了什么?没错,其实lambda表达式的参数列表就是对应的函数式接口的抽象方法的参数列表,并且类型可以省略(编译器自动推断),然后n1.compareTo(n2)的返回值是int类型,也符合compare()的方法描述。这样就算是把lambda表达式和接口的抽象方法签名匹配成功了,不会出现编译错误。
除此之外,Runnable也是一个函数式接口,它只有一个抽象方法,即run(),run()方法的方法签名如下所示:
public abstract void run();
不接受任何参数,也没有返回值。那如果要编写对应的lambda表达式,该如何做呢?其实非常简单,下面是一个示例:
Runnable r = () -> {
System.out.println(Thread.currentThread().getName());
//do something
};
如果观察仔细的话,会发现,示例代码中把这个lambda表达式赋值给了Runnable类型的变量r!经过上面的讨论,我们知道,其实lambda就是一个方法实现(其实叫做函数会更加合适),这条赋值语句看起来就好像是再说:“把方法(函数)赋值给变量!”。如果没有接触过函数式编程,会觉得这样很奇怪,怎么能把方法赋值给变量呢?计算机就是这样有意思,总是有各种各样奇奇怪怪的东西冲击我们的思维!那这有什么用呢?咱先不说什么高阶函数,科里化啥的(这些是函数式编程里的概念),就说一点:意味着我们可以把方法(函数)当做变量来使用!即现在方法就是Java世界里的“一等公民”了!既可以将其作为参数传递给其他方法(函数),还可以将其作为其他方法(函数)的返回值(以后会讲到具体的案例)
4 策略模式
策略模式是著名的23种设计模式中的一种,关于它的描述,我这里就不多说了。直接来看个例子吧。
例子是这样的,现在有一个代表汽车的Car类以及一个Car列表,现在我们想要筛选列表中符合要求的汽车,为了应对多变的筛选方法,我们打算用策略模式来实现功能。
下面是Car类的代码:
public class Car {
//品牌
private String brand;
//颜色
private Color color;
//车龄
private Integer age;
//三个参数的构造函数以及setter和getter
//颜色的枚举
public enum Color {
RED,WHITE,PINK,BLACK,BLUE;
}
}
//包含Car对象的列表
List cars = Arrays.asList(
new Car("BWM",Car.Color.BLACK, 2),
new Car("Tesla", Car.Color.WHITE, 1),
new Car("BENZ", Car.Color.RED, 3),
new Car("Maserati", Car.Color.BLACK,1),
new Car("Audi", Car.Color.PINK, 5));
我们希望用一个方法来封装筛选的逻辑,其方法签名伪代码如下所示:
cars carFilter(cars, filterStrategy);
接下来实现策略模式,下面是相关的代码:
public interface CarFilterStrategy {
boolean filter(Car car);
}
public class BWMCarFilterStrategy implements CarFilterStrategy {
@Override
public boolean filter(Car car) {
return "BWM".equals(car.getBrand());
}
}
public class RedColorCarFilterStrategy implements CarFilterStrategy {
@Override
public boolean filter(Car car) {
return Car.Color.RED.equals(car.getColor());
}
}
为了简单,仅仅实现了两种筛选策略,第一种是删选出品牌是“BWM”的汽车,第二种是删选出颜色为红色的汽车。最后来实现carFilter方法,如下所示:
private static List carFilter(List cars, CarFilterStrategy strategy) {
List filteredCars = new ArrayList<>();
for (Car car : cars) {
if (strategy.filter(car)) {
filteredCars.add(car);
}
}
return filteredCars;
}
最后的最后是测试代码:
public static void main(String[] args) {
System.out.println(carFilter(cars, new BWMCarFilterStrategy()));
System.out.println("----------------------------------------");
System.out.println(carFilter(cars, new RedColorCarFilterStrategy()));
}
分别实例化两个策略,将其作为参数传递给carFilter()方法,最终的输出如下所示:
[Car{brand='BWM', color=BLACK, age=2}]
----------------------------------------
[Car{brand='BENZ', color=RED, age=3}]
确实符合预期。是不是就到此为止了呢?当然不!我们发现,其实BWMCarFilterStrategy以及RedColorCarFilterStrategy的实现代码都非常简单,仅仅寥寥几行代码,而且CarFilterStrategy接口仅仅有一个filter抽象方法,显然是一个函数式接口,那我们能不能用lambda表达式来简化呢?答案是:完全可以!而且更加推荐用lambda表达式来简化这种情况。
4.1 用lambda表达式来简化代码
只要略微做一些修改就行了:
System.out.println(carFilter(cars, car -> "BWM".equals(car.getBrand())));
System.out.println("----------------------------------------");
System.out.println(carFilter(cars, car -> Car.Color.RED.equals(car.getColor())));
这里不再使用BWMCarFilterStrategy以及RedColorCarFilterStrategy两个类了,直接用lambda表达式就行了!最后把这俩实现删除掉!是不是顿时感觉整个项目的代码清爽了许多?
4.2 需要注意的
其实本小节的例子有些过于特殊了,如果你项目中的策略模式的实现非常复杂,其策略不是简简单单的几行代码就能解决的,此时要么进一步封装代码,要么就最好不要用lambda表达式了,因为如果逻辑复杂的话,强行使用lambda不仅仅不能简化代码,反而会使得代码更加晦涩。
5 方法引用
最后简单讲一下方法引用吧,方法引用其实是lambda表达式的一种特殊情况的表示,语法规则是:
:
如果lambda表达式的主体逻辑仅仅是一个调用方法的语句的话,那么就可以将其转换为方法引用,如下所示:
//普通的lambda表达式
numbers.forEach(n -> System.out.println(n));
//转换成方法引用
numbers.forEach(System.out::println);
他俩效果是完全一样的,但显然方法引用更加简洁,语义也更加明确了,这一语法糖“真香!”。具体的我就不多说了,建议看看《Java8 实战》一书,里面有非常非常详细的介绍。
6 小结
本文简单介绍了lambda表达式的语法以及使用。lambda表达式确实能大大简化原本复杂啰嗦的Java代码,而且更加灵活,语义也更加清晰明了,写代码的时候就好像用自然语言和计算机对话一样!但也不是哪里都能使用的,一个最基本的要求就是:其放置的位置要对应着一个函数式接口。函数式接口即只有一个抽象方法的接口,例如Comparator,Runnable等。除此之外,使用lambda表达式的时候,其主体逻辑最好不要超过10行,否则最好还是换一种方式来实现,这里10行并不是那么严格,具体情况还要具体分析。方法引用是一种特殊情况下的lambda表达式的表示方法,可以理解为是lambda的一个语法糖,其语义更加明确,语法也更加简洁,用起来还是非常舒服的!
最后,作为一个补充,来简单看看JDK内置的一些通用性比较强的函数式接口,这些接口都在java.util.function包下,我没数过,咋一看估计得有40多个吧。常用的有Function,Predicate,Consumer,Supplier等。Function的抽象方法的方法签名如下所示:
R apply(T t); //T,R是泛型
简单从语义上来看,就是传入一个T类型的值,然后apply函数将其转换成R类型的值,即一对一映射。其他的接口就不做介绍了。
7 参考资料
《Java8 实战》
java se基础巩固实例,Java SE基础巩固(十五):lambda表达式相关推荐
- java语言程序设计基础篇课后答案_《Java语言程序设计:基础篇》课后复习题答案-第十五章.pdf...
<Java语言程序设计:基础篇>课后复习题答案-第十五章 Chapter15Graphics 1. Theycoordinateshouldincreaseandthexcoordinat ...
- Java从入门到精通十四(Lambda表达式)
Java从入门到精通十四(Lambda表达式) Lambda的引入体验 实例一(抽象方法无参无返回值) 实例二(抽线方法有参无返回值) 实例三(抽象方法带参带返回值) lambda的表达式的简化操作 ...
- java基础语法实例教程_Java 基础语法
一个 Java 程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作 面向对象中的一些概念 下表列出了 面向对象 编程中的一些概念 名词 说明 对象 对象是类的一个实例,有状态和行为 ...
- java 文件路径表达式_Java基础(二十二) Lambda表达式和File类
函数式编程思想 强调的是做什么,而不是以什么样的方式来做,它忽略了面向对象的复杂语法,只要能够获取到结果,谁去做的,怎么做的,都不重要,重要的是结果,不重视过程. 冗余的Runnable代码 传统的写 ...
- java写exe程序实例,java实现可安装的exe程序实例详解
java实现可安装的exe程序实例详解 通过编写java代码,实现可安装的exe文件的一般思路: 1.在eclipse中创建java项目,然后编写java代码,将编写好的java项目导出一个.jar格 ...
- matlab基础与实例教程,MATLAB基础与实例教程
系统全面,实例丰富 考虑到Matlab进行仿真和运算分析时的基础知识和实践操作,讲解从基础的变量.函数.数据类型等入手,涉及到数学分析.图形可视化.Simulink仿真.文件读写等,全面地介绍了Mat ...
- Java 8:在新的Nashorn JS引擎中编译Lambda表达式
在最近的一篇文章中,我了解了Java 8和Scala如何实现Lambda表达式. 众所周知,Java 8不仅引入了对Javac编译器的改进,而且还引入了全新的解决方案-Nashorn. 这个新引擎旨在 ...
- Java JVM 动态方法调用指令 invokedynamic 实现分析(以 Lambda 表达式实现原理为例)...
一.前言 对于 invokedynamic 指令的实现需要方法句柄作为前提知识点.可参考 Java JVM 动态方法调用之方法句柄 MethodHandle. 本文以 Lambda 表达式中运用 in ...
- lambda表达式java项目常用_一文带你彻底搞懂Lambda表达式
1. 为什么使用Lambda表达式 Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递).可以写出更简洁.更灵活的代码.作为一种更紧凑的代码风 ...
最新文章
- 在Ubuntu下FFmpeg编译,支持x264和x265(HECV)
- 猴子请来的逗比项目流水总结
- 使用pdf.js预览实现读取服务器外部文件
- [ARM异常]-ARMV8-aarch64 异常(中断)是如何跳转到向量表的
- python教程:函数递归与生成器教程
- jvm 06-G1收集器
- 量化指标公式源码_通达信指标公式源码线上阴线指标公式
- hive2 java连接_用Java代码通过JDBC连接Hiveserver2
- 免费zblog mip主题aymFreeTwo
- 2021年CBA总决赛第二场预测
- Linux工作笔记022---查看Centos 内核版本号
- nginx开启目录浏览,解决中文乱码问题
- layer——极简的jquery弹出层插件
- iOS中的所有字体和UILabel
- gqview的安装与汉化
- XMPP即时通讯协议使用(前传)——协议详解
- 如何清空c盘只剩系统_怎么把C盘东西都删除只留系统东西
- 不使用vue-cli 搭建vue项目
- ENVI操作:监督分类
- win10 wifi图标不见了 修复办法
热门文章
- Hive,Hbase shell 中文变问号(??) 的解决方法
- linux 下执行python.py 无效解决方案
- 【实践】58同城本地服务推荐系统演进
- 飞猪信息流内容推荐探索
- 【报告分享】2020年上半年中国直播电商行业发展分析报告.pdf(附下载链接)...
- 全球首发!惯性导航导论(剑桥大学)第十一部分
- oracle hcm 发展,甲骨文发布Oracle HCM Cloud云服务 呈现三大亮点
- cdh mysql sqoop 驱动_[bigdata-003]在cdh 5.7下 用sqoop1将mysql数据库数据导入到hdfs的方式...
- 6大Facebook广告文案绝招提升你Shopify独立站的转化率
- linux shell解析1