Java8 新特性Lambda表达式实例详解
发布时间 - 2026-01-10 23:27:06 点击率:次Java8 新特性Lambda表达式实例详解

在介绍Lambda表达式之前,我们先来看只有单个方法的Interface(通常我们称之为回调接口):
public interface OnClickListener {
void onClick(View v);
}
我们是这样使用它的:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
v.setText("lalala");
}
});
这种回调模式在各种框架中非常流行,但是像上面这样的匿名内部类并不是一个好的选择,因为:
- 语法冗余;
- 匿名内部类中的this指针和变量容易产生误解;
- 无法捕获非final局部变量;
- 非静态内部类默认持有外部类的引用,部分情况下会导致外部类无法被GC回收,导致内存泄露。
令人高兴的是Java8为我们带来了Lambda,下面我们看看利用Lambda如何实现上面的功能:
button.setOnClickListener(v -> v.setText("lalala"));
怎么样?!五行代码用一行就搞定了!!!
在这里补充个概念函数式接口;前面提到的OnClickListener接口只有一个方法,Java中大多数回调接口都有这个特征:比如Runnable和Comparator;我们把这些只拥有一个方法的接口称之为函数式接口。
一、Lambda表达式
匿名内部类最大的问题在于其冗余的语法,比如前面的OnClickListener中五行代码仅有一行是在执行任务。Lambda表达式是匿名方法,前面我们也看到了它用极其轻量的语法解决了这一问题。
下面给大家看几个Lambda表达式的例子:
(int x, int y) -> x + y //接收x和y两个整形参数并返回他们的和
() -> 66 //不接收任何参数直接返回66
(String name) -> {System.out.println(name);} //接收一个字符串然后打印出来
(View view) -> {view.setText("lalala");} //接收一个View对象并调用setText方法
Lambda表达式语法由参数列表、->和函数体组成。函数体既可以是一个表达式也可以是一个代码块。
表达式:表达式会被执行然后返回结果。它简化掉了return关键字。
代码块:顾名思义就是一坨代码,和普通方法中的语句一样。
二、目标类型
通过前面的例子我们可以看到,lambda表达式没有名字,那我们怎么知道它的类型呢?答案是通过上下文推导而来的。例如,下面的表达式的类型是OnClickListener
OnClickListener listener = (View v) -> {v.setText("lalala");};
这就意味着同样的lambda表达式在不同的上下文里有不同的类型
Runnable runnable = () -> doSomething(); //这个表达式是Runnable类型的 Callback callback = () -> doSomething(); //这个表达式是Callback类型的
编译器利用lambda表达式所在的上下文所期待的类型来推导表达式的类型,这个被期待的类型被称为目标类型。lambda表达式只能出现在目标类型为函数式接口的上下文中。
Lambda表达式的类型和目标类型的方法签名必须一致,编译器会对此做检查,一个lambda表达式要想赋值给目标类型T则必须满足下面所有的条件:
- T是一个函数式接口
- lambda表达式的参数必须和T的方法参数在数量、类型和顺序上一致(一一对应)
- lambda表达式的返回值必须和T的方法的返回值一致或者是它的子类
- lambda表达式抛出的异常和T的方法的异常一致或者是它的子类
由于目标类型是知道lambda表达式的参数类型,所以我们没必要把已知的类型重复一遍。也就是说lambda表达式的参数类型可以从目标类型获取:
//编译器可以推导出s1和s2是String类型
Comparator<String> c = (s1, s2) -> s1.compareTo(s2);
//当表达式的参数只有一个时括号也是可以省略的
button.setOnClickListener(v -> v.setText("lalala"));
ps: Java7中的泛型方法和<>构造器也是通过目标类型来进行类型推导的,如:
List<Integer> intList = Collections.emptyList>(); List<String> strList = new ArrayList<>();
三、作用域
在内部类中使用变量名和this非常容易出错。内部类通过继承得到的成员变量(包括来说object的)可能会把外部类的成员变量覆盖掉,未做限制的this引用会指向内部类自己而非外部类。
而lambda表达式的语义就十分简单:它不会从父类中继承任何变量,也不用引入新的作用域。lambda表达式的参数及函数体里面的变量和它外部环境的变量具有相同的语义(this关键字也是一样)。
下面我们举个栗子吧!
public class HelloLambda {
Runnable r1 = () -> System.out.println(this);
Runnable r2 = () -> System.out.println(toString());
@Override
public String toString() {
return "Hello, lambda!";
}
public static void main(String[] args) {
new HelloLambda().r1.run();
new HelloLambda().r2.run();
}
}
上面的代码最终会打印两个Hello, lambda!,与之相类似的内部类则会打印出类似HelloLambda$1@32a890和HelloLambda$1@6b32098这种出乎意料的字符串。
总结:基于词法作用域的理念,lambda表达式不可以掩盖任何其所在上下文的局部变量。
四、变量捕获
在Java7中,编译器对内部类中引用的外部变量(即捕获的变量)要求非常严格:如果捕获的变量没有被声明为final就会产生一个编译错误。但是在Java8中放宽了这一限制–对于lambda表达式和内部类,允许在其中捕获那些符合有效只读的局部变量(如果一个局部变量在初始化后从未被修改过,那么它就是有效只读)。
Runnable getRunnable(String name){
String hello = "hello";
return () -> System.out.println(hello+","+name);
}
对于this的引用以及通过this对未限定字段的引用和未限定方法的调用本质上都属于使用final局部变量。包含此类引用的lambda表达式相当于捕获了this实例。在其他情况下,lambda对象不会保留任何对this的应用。
这个特性对内存管理是极好的:要知道在java中一个非静态内部类会默认持有外部类实例的强引用,这往往会造成内存泄露。而在lambda表达式中如果没有捕获外部类成员则不会保留对外部类实例的引用。
不过尽管Java8放宽了对捕获变量的语法限制,但试图修改捕获变量的行为是被禁止的,比如下面这个例子就是非法的:
int sum = 0;
list.forEach(i -> {sum += i;});
为什么要禁止这种行为呢?因为这样的lambda表达式很容易引起race condition
lambda表达式不支持修改捕获变量的另外一个原因是我们可以使用更好的方式来实现同样的效果:使用规约(condition)。java.util.stream包提供了各种规约操作,关于Java8中的Stream API我们放到下一章介绍。
五、方法引用
lambda表达式允许我们定义一个匿名方法,并以函数式接口的方式使用它。Java8能够在已有的方法上实现同样的特性。
方法引用和lambda表达式拥有相同的特性(他们都需要一个目标类型,并且需要被转化为函数式接口的实例),不过我们不需要为方法引用提供方法体,我们可以直接通过方法名引用已有方法。
以下面的代码为例,假设我们要按照userName排序
class User{
private String userName;
public String getUserName() {
return userName;
}
...
}
List<User> users = new ArrayList<>();
Comparator<User> comparator = Comparator.comparing(u -> u.getUserName());
Collections.sort(users, comparator);
我们可以用方法引用替换上面的lambda表达式
Comparator<User> comparator = Comparator.comparing(User::getUserName);
这里的User::getUserName被看做是lambda表达式的简写形式。尽管方法引用不一定会把代码变得更紧凑,但它拥有更明确的语义–如果我们想要调用的方法拥有一个名字,那么我们就可以通过方法名调用它。
方法引用有很多种,它们的语法如下:
- 静态方法引用:ClassName::methodName
- 实例上的实例方法引用:instanceReference::methodName
- 超类上的实例方法引用:super::methodName
- 类型上的实例方法引用:ClassName::methodName
- 构造方法引用:Class::new
- 数组构造方法引用:TypeName[]::new
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
# Java8新特性Lambda表达式
# java
# Lambda表达式详解
# java8
# 新特性
# Java8新特性lambda表达式有什么用(用法实例)
# Java8新特性Lambda表达式的一些复杂用法总结
# Java8新特性之Lambda表达式浅析
# Java8中lambda表达式的应用及一些泛型相关知识
# Java8 Lambda表达式详解及实例
# Java8中的 Lambda表达式教程
# Java8与Scala中的Lambda表达式深入讲解
# Java8中的lambda表达式入门教程
# 30分钟入门Java8之lambda表达式学习
# Java8中Lambda表达式的理解与应用
# 是一个
# 类中
# 这一
# 回调
# 子类
# 或者是
# 只有一个
# 会把
# 称之为
# 返回值
# 的是
# 情况下
# 他们的
# 几个
# 就会
# 是在
# 都有
# 在这里
# 是这样
# 已有
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
黑客如何利用漏洞与弱口令入侵网站服务器?
laravel怎么用DB facade执行原生SQL查询_laravel DB facade原生SQL执行方法
C#如何调用原生C++ COM对象详解
谷歌浏览器下载文件时中断怎么办 Google Chrome下载管理修复
Windows Hello人脸识别突然无法使用
php中::能调用final静态方法吗_final修饰静态方法调用规则【解答】
java中使用zxing批量生成二维码立牌
独立制作一个网站多少钱,建立网站需要花多少钱?
Laravel如何使用Collections进行数据处理?(实用方法示例)
如何快速搭建虚拟主机网站?新手必看指南
canvas 画布在主流浏览器中的尺寸限制详细介绍
如何在万网主机上快速搭建网站?
Laravel DB事务怎么使用_Laravel数据库事务回滚操作
如何快速搭建安全的FTP站点?
Laravel怎么实现模型属性的自动加密
济南网站建设制作公司,室内设计网站一般都有哪些功能?
Linux后台任务运行方法_nohup与&使用技巧【技巧】
Win11怎么设置虚拟桌面 Win11新建多桌面切换操作【技巧】
怎么制作一个起泡网,水泡粪全漏粪育肥舍冬季氨气超过25ppm,可以有哪些措施降低舍内氨气水平?
网站页面设计需要考虑到这些问题
Laravel怎么进行数据库回滚_Laravel Migration数据库版本控制与回滚操作
深圳网站制作培训,深圳哪些招聘网站比较好?
如何在Windows服务器上快速搭建网站?
如何确保西部建站助手FTP传输的安全性?
JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)
大同网页,大同瑞慈医院官网?
西安专业网站制作公司有哪些,陕西省建行官方网站?
Laravel Eloquent性能优化技巧_Laravel N+1查询问题解决
网站制作公司哪里好做,成都网站制作公司哪家做得比较好,更正规?
html如何与html链接_实现多个HTML页面互相链接【互相】
如何用PHP快速搭建高效网站?分步指南
Laravel如何处理文件下载请求?(Response示例)
在线教育网站制作平台,山西立德教育官网?
Win11怎样安装网易有道词典_Win11安装词典教程【步骤】
Laravel如何使用缓存系统提升性能_Laravel缓存驱动和应用优化方案
免费网站制作appp,免费制作app哪个平台好?
html5源代码发行怎么设置权限_访问权限控制方法与实践【指南】
公司网站制作价格怎么算,公司办个官网需要多少钱?
零基础网站服务器架设实战:轻量应用与域名解析配置指南
如何基于PHP生成高效IDC网络公司建站源码?
Laravel如何实现文件上传和存储?(本地与S3配置)
如何在IIS服务器上快速部署高效网站?
如何快速生成ASP一键建站模板并优化安全性?
php嵌入式断网后怎么恢复_php检测网络重连并恢复硬件控制【操作】
Python3.6正式版新特性预览
laravel服务容器和依赖注入怎么理解_laravel服务容器与依赖注入解析
Laravel Eloquent模型如何创建_Laravel ORM基础之Model创建与使用教程
高防服务器租用首荐平台,企业级优惠套餐快速部署
Linux系统命令中screen命令详解
如何选择PHP开源工具快速搭建网站?

