乐字节-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表达式(也称为闭包),它允许我们将函数当成参数传递给某个方法,或者把代码本身当作 ...
随机推荐
- CDC之Metastability
1 CDC A clock domain crossing occurs whenever data is transferred from a flop driven by one clock t ...
- MyEclipse获取注册码
最近刚装上MyEclipse,一直弹窗提示注册码过期,开始还能接受,到最后,每发布一个项目便弹窗提醒,顿时感觉烦了,得治理治理这个烦人的注册码,下面是一段自动生成注册名和注册码的代码,只需要直接拿来用 ...
- hadoop单机环境配置
1.配置一台linux服务器(当前使用CentOS7)及一些基本设置 1.1在wmware下制作一台centos服务器(桥接模式) 并设置静态ip (编辑/etc/sysmconfig ...
- windows常用的cmd命令和常用操作。
这几日部署Jenkins,牵扯到很多东西,比如用到许多cmd命令和Linux命令.查找比较花时间,因此将查看的文档留下,以避免下次重新查找浪费时间. Windows cmd命令: http://blo ...
- SLAM:ORB-SLAM 位姿优化描述
只知道算法描述和代码,而不知道原理是比较扯的事情,还是把原理转载一下. 原文链接: http://www.cnblogs.com/luyb/p/5447497.html ORB-SLAM作为单目SLA ...
- react基础篇五
再看JSX 本质上来讲,JSX 只是为 React.createElement(component, props, ...children) 方法提供的语法糖.比如下面的代码: <MyButto ...
- antd #upload
import React from 'react' import {Upload, Icon, message,Button } from 'antd' import './index.scss'; ...
- kipmi0进程单核CPU100%的解决办法
top查看服务器进程,发现有个kipmi0的进程竟然CPU的单核占用高达100%,而且居高不下. 于是上网搜了搜大家的说法了给出的链接,大概意思是一个固件问题,可以通过修改文件来解决. 专业的解释地址 ...
- Python中字典的相关操作
1. Python类似于Java中的哈希表,只是两种语言表示的方式是不一样的,Python中的字典定义如下: 在Python中是一种可变的容器模型,它是通过一组键(key)值(value)对组成,这种 ...
- BZOJ 4195: [Noi2015]程序自动分析 并查集 + 离散化 + 水题
TM 读错题了...... 我还以为是要动态询问呢,结果是统一处理完了再询问...... 幼儿园题,不解释. Code: #include<bits/stdc++.h> #define m ...