乐字节-Java8核心特性实战之Lambda表达式
大家好,小乐又来给大家分享Java8核心特性了,上一篇文章是《乐字节|Java8核心实战-接口默认方法》,这次就来讲Java8核心特征之Lambda表达式。
Java8 引入Lambda表达式,允许开发者将函数当成参数传递给某个方法,或者把代码本身当作数据进行处理。使用Lambda表达式,使得应用变得简洁而紧凑。 很多语言(Groovy、Scala等)从设计之初就支持Lambda表达式。但是java中使用的是 匿名内部类代替。最后借助强大的社区力量,找了一个折中的Lambda实现方案,可以实现简洁而紧凑的语言结构。

1、匿名内部类到Lambda的演化
匿名内部类,即一个没有名字的,存在于一个类或方法内部的类。当我们需要用某个类且只需要用一次,创建和使用和二为一时,我们可以选择匿名内部类,省掉我们定义类的步骤。
匿名内部类会隐士的继承一个类或实现一个接口,或者说匿名内部类是一个继承了该类或者实现了该接口的子类匿名对象。下面看一个匿名内部类的例子
package com.java8;
/*
定义和使用匿名内部类
*/
public class NoNameClass {
public static void main(String[] args) {
Model m = new Model(){
@Override
public void func() {
System.out.println("方法的实现");
}
};
m.func();
}
}
// 需要被实现的接口
interface Model{
void func();
}
等价的Lambda 代码
package com.java8;
/*
定义和使用Lambda 简化代码
*/
public class NoNameClass {
public static void main(String[] args) {
Model m = new Model(){()->{
System.out.println("方法的实现");
}};
m.func();
}
}
可以看出使用Lambda 表达式替代了匿名内部类代码,使得代码更加简化、紧凑。
语法
(parameters) -> expression 或 (parameters) ->{ statements; }
- 可选类型声明
不需要声明参数类型,编译器可以统一识别参数值。 - 可选的参数圆括号
一个参数无需定义圆括号,但多个参数需要定义圆括号。 - 可选的大括号
如果主体包含了一个语句,就不需要使用大括号。 - 可选的返回关键字
如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指明表达式返回了一个数值
Lambda 表达式示例:

2、Lambda使用形式
使用Lambda时,实现方法可以有参数,也可以有返回值,如果没指定参数类型,则由编译器自行推断得出。
无参带返回值
生成[1,10]之间的任意整数
interface Model2{
int func();
}
Model2 md2 = () -> {return (int)(Math.random()*10+1)};
Lambda的改写需要有对应的抽象方法,当没有参数时需要使用()占位,当表达式只有一行代码时,可以省略return和{}
以上的Lambda等价于:
Model2 md2 = () -> (int)(Math.random()*10+1);
带参带返回值
返回一个对数字描述的字符串。
interface Model3{
String func(int a);
}
Model3 md3 = (int a) -> {
return "This is a number " + a;
};
形参写在()内即可,参数的类型可以省略,此时将由编译器自行推断得出,同时还可以省略()
以上的Lambda等价于:
md3 = a -> "This is a number " + a;
省略了参数类型,小括号,同时连带实现体的括号和return一并省去。
带多个参数
根据输入的运算符计算两个数的运算,并返回结果
interface Model4{
String func(int a, int b, String oper);
}
Model4 md4 = (a, b, s) -> {
String res = "";
if("+".equals(s)){
res = ( a+b ) + "";
}else if("-".equals(s)){
res = ( a-b ) + "";
}else if("*".equals(s)){
res = ( a*b ) + "";
}else if("/".equals(s)){
res = ( a/b ) + ""; // 暂不考虑除0的情况
}else{
res = "操作有失误";
}
return res;
};
System.out.println(md4.func(1,1,"+"));
以上例子为多个参数的Lambda表达式,其中省略掉了每一个参数的类型,编译器自动推断。多条语句时实现体的{}不能省。
3、Lambda作为参数
在Java8之前,接口可以作为方法参数传入,执行时必须提供接口实现类的实例。从java8开始,Lambda可以作为接口方法实现,当作参数传入,无论从形式上还是实际上都省去了对象的创建。使代码更加的紧凑简单高效。
- 定义接口
在接口中,必须有且仅有一个抽象方法,以确定Lambda模板
// 无参无返回值的方法
interface LambdaInterface1{
void printString();
}
// 带参无返回值的方法
interface LambdaInterface2{
void printString(String str);
}
定义方法接收参数
在某方法中需要使用接口作为参数
// 无参
public static void testLambda(LambdaInterface1 lam1){
lam1.printString();
}
// 带参
public static void testLambda2(String s,LambdaInterface2 lam2){
lam2.printString(s);
}
- Lambda表达式作为参数传入
// 无参Lambda作为参数
testLambda(()->{
System.out.println("可以简单,可以复杂");
});
// 带参Lambda作为参数
testLambdaParam("hello",(a)->{
System.out.println(a);
});
4、Lambda中使用变量
在Lambda中可以定义自己的局部变量,也可以使用外层方法的局部变量,还可以使用属性。这一点也不难理解,既然是一个方法的实现,只写了一个代码块,那么使用本身所属方法的局部变量和类的属性也并不过分。
public static void main(String[] args) {
List<String> strs = new ArrayList<String>(){
{
add("aaa");
add("bbb");
add("ccc");
}
};
int j = 1;
strs.forEach((str)->{
int i = 0;
System.out.println(str + " " + i + " " + j);
});
}
5、Lambda类型推断
- 类型检查
Lambda的类型是从使用Lambda的上下文推断出来的。 Lambda表达式的参数与函数式接口内方法的参数,返回值类型相互对应。Lambda表达式需要的类型,或者说Lambda实现的那个函数式接口称之为目标类型。 - 类型推断
利用目标类型来检查一个Lambda是否可以用于某个特定的上下文,推断Lambda参数的类型。
6、Lambda表达式实战
热销商品排序

排序对于久经开发的你来说可能并不陌生,假如原来你做过电商项目,相信对于电商场景下的商品记录排序操作很有感情,下面我们使用Lambda 来看看热销商品排序的操作。
测试数据这里以手机测试数据为例
/**
* 实际开发数据通常从数据库获取
* 这里使用测试数据
*/
Goods g01=new Goods(1,"小米9",1789,200, BigDecimal.valueOf(2500));
Goods g02=new Goods(2,"华为Mate20",5000,3000, BigDecimal.valueOf(7000));
Goods g03=new Goods(3,"OPPO R17",2000,2827, BigDecimal.valueOf(1500));
Goods g04=new Goods(4,"魅族 Note9",2000,1600, BigDecimal.valueOf(1600));
Goods g05=new Goods(5,"一加6T",8000,5000, BigDecimal.valueOf(3500));
List<Goods> goods= Arrays.asList(g01,g02,g03,g04,g05);
- Collections.sort 静态方法实现排序
Collections.sort(goods,(g1,g2)->g1.getSale()-g2.getSale());
- List.sort 默认方法实现集合排序

// 使用Lambda 对商品记录按销量进行排序
goods.sort((g1,g2)->g1.getSale()-g2.getSale());
Stream.sorted 方法实现元素排序
// 多个条件排序情况 Lambda 配置Stream 销量+价格排序 销量相等时按照价格排序
goods =goods.stream().sorted((g1,g2)->g1.getSale()-g2.getSale())
.sorted((g1,g2)->g1.getPrice().compareTo(g2.getPrice()))
.collect(Collectors.toList());
日志输出优化

对于项目开发日志打印是一项不可获取的模块,无论实在开发阶段还是项目部署上线后,日志信息的输出对于开发人员来以及运维人员来说都是一项重要的参考指标。
日志输出场景这里以用户模块UserService 为例,以下为优化前的日志输出代码:
public String login(String userName, String userPwd) {
logger.info("UserService 接收到参数-->" + userName + "," + userPwd);
/**
* 登录逻辑省略
*/
return "login";
}
日志级别设置到debug,在开发阶段方便查看后端接收到的参数信息。仔细分析这里的日志代码,可以看到当日志级别设置为info 时 debug 日志不应该执行输出操作,同时这里调用debug 方法时,对于传入的字符串参数需要作对应的拼接操作,才会传入过来。当访问的情况在商城项目做活动情况下 这里的情况有可能会变得很糟糕:所有的debug 信息全部输出 同时会有大量字符串拼接操作,会影响整个应用程序的执行性能。
日志输出场景这里以用户模块UserService 为例,日志输出代码优化
- 输出日志前判断日志输出级别
- 借助Lambda延迟日志内容输出
/**
* 添加info方法
* 判断日志打印级别
* 当条件成立时 输出日志信息
* @param logger
* @param message
*/
public void info(Log logger, Supplier<String> message){
if(logger.isInfoEnabled()){
logger.info(message.get());
}
}
public String login(String userName, String userPwd) {
//logger.info("UserService 接收到参数-->" + userName + "," + userPwd);
// 延迟Lambda 表达式执行 只有确定
info(logger,()->"UserService 接收到参数-->" + userName + "," + userPwd);
return "login";
}
7、Lambda优势与使用场景
Lambda表达式的引入取代了匿名内部类,使得代码变得简洁、紧凑,同时Lambda的惰性特点,在开发时能够提高应用程序的执行性能。
对于Lambda的应用场景,从代码结构来说通常是结合函数式接口来使用,使得开发是面向函数来进行编程,也是Java8引入的一种新的思想-函数式编程(后续会介绍)。同时还会结合前面讲到的接口默认方法提现到应用开发中。
乐字节原创文章,转载请注明出处和作者!
更多乐字节技术资料教程请来: 乐字节 腾讯课堂
乐字节-Java8核心特性实战之Lambda表达式的更多相关文章
- 乐字节-Java8核心特性实战-接口默认方法
JAVA8已经发布很久,是自java5(2004年发布)之后Oracle发布的最重要的一个版本.其中包括语言.编译器.库.工具和JVM等诸多方面的新特性,对于国内外互联网公司来说,Java8是以后技术 ...
- 乐字节-Java8核心特性实战之Stream(流)
说起流,我们会想起手机 ,电脑组装流水线,物流仓库商品包装流水线等等.如果把手机 ,电脑,包裹看做最终结果的话,那么加工商品前的各种零部件就可以看做数据源,而中间一系列的加工作业操作,就可以看做流的处 ...
- 乐字节-Java8核心特性实战之函数式接口
什么时候可以使用Lambda?通常Lambda表达式是用在函数式接口上使用的.从Java8开始引入了函数式接口,其说明比较简单:函数式接口(Functional Interface)就是一个有且仅有一 ...
- 乐字节Java8核心特性之方法引用
大家好,我是乐字节的小乐,上一次我们说到了Java8核心特性之函数式接口,接下来我们继续了解Java8又一核心特性--方法引用. Java8 中引入方法引用新特性,用于简化应用对象方法的调用, 方法引 ...
- 乐字节Java8核心特性之Optional类
大家好啊,上次小乐给大家介绍了Java8最最重要的一个特性——Stream流,点击可以回顾哦. Optional<T>类(java.util.Optional)是一个容器类,代表一个值存在 ...
- 乐字节-Java8新特性-接口默认方法
总概 JAVA8 已经发布很久,而且毫无疑问,java8是自java5(2004年发布)之后的最重要的版本.其中包括语言.编译器.库.工具和JVM等诸多方面的新特性. Java8 新特性列表如下: 接 ...
- 乐字节-Java8新特性之Base64和重复注解与类型注解
上一篇小乐给大家说了<乐字节-Java8新特性之Date API>,接下来小乐继续给大家说一说Java8新特性之Base64和重复注解与类型注解. 一.Base64 在Java 8中,内置 ...
- 乐字节-Java8新特性-接口默认方法之Stream流(下)
接上一篇:<Java8新特性之stream>,下面继续接着讲Stream 5.流的中间操作 常见的流的中间操作,归为以下三大类:筛选和切片流操作.元素映射操作.元素排序操作: 操作 描述 ...
- 乐字节-Java8新特性-Lambda表达式
上一篇文章我们了解了Java8新特性-接口默认方法,接下来我们聊一聊Java8新特性之Lambda表达式. Lambda表达式(也称为闭包),它允许我们将函数当成参数传递给某个方法,或者把代码本身当作 ...
随机推荐
- 《深入理解Android虚拟机内存管理》示例程序编译阶段生成的各种语法树完整版
1.tokens "int" "int" <SPACES> " &quo ...
- Excel常用的小技巧
1.Excel如何实现单元格内轻松换行:按住ALT+enter就可以了. 2.Excel固定表头:在“视图”>冻结窗口>冻结首行. 3.防止电脑突然断电,导致正在编辑的Excel数据丢失, ...
- (转)Bootstrap 之 Metronic 模板的学习之路 - (5)主题&布局配置
https://segmentfault.com/a/1190000006736457 Theme Setup 主题配置 Metronic comes with 6 color themes,defa ...
- node、Mongo项目如何前后端分离提供接口给前端
node接口编写,vue-cli代理接口方法 通常前端使用的MocK 数据的方法,去模拟假的数据,但是如果有node Mongodb 去写数据的话就不需要在去mock 数据了,具体的方法如下. 首先 ...
- 两个控件同一行显示bootstrap
<div class="form-group"> <label for="Name" class="form-inline" ...
- 08.Web服务器-2.HTTP协议介绍
HTTP是Hyper Text Transfer Protocol(超文本传输协议)的缩写.它的发展是万维网协会(World Wide Web Consortium)和Internet工作小组IETF ...
- nginx下部署showdoc
1. 安装nginx服务器sudo apt-get install nginx -y 2. 启动服务sudo service nginx start 3. 安装php环境 sudo apt-get i ...
- (19)Spring Boot 添加JSP支持【从零开始学Spring Boot】
[来也匆匆,去也匆匆,在此留下您的脚印吧,转发点赞评论: 您的认可是我最大的动力,感谢您的支持] 看完本文章您可能会有些疑问,可以查看之后的一篇博客: 81. Spring Boot集成JSP疑问[从 ...
- Spring Cloud-Ribbon ILoadBalancer负载均衡器核心源码(四)
Ribbon负载均衡相关类 AbstractloadBalancer ILoadBalancer的抽象实现类 public abstract class AbstractLoadBalancer im ...
- mysql5.7忘记root密码
systemctl stop mysqld.service vi /etc/my.cnf [mysqld] skip-grant-tables skip-networking mysql -uroot ...