写点什么

Java 10 将带来升级版的 Lambda

  • 2017-01-22
  • 本文字数:2462 字

    阅读完需:约 8 分钟

一个新的JEP 将用于增强lambda 功能,提出的更改包括更好的消岐、对未使用的参数用下划线标注和外部变量的跟踪。虽然这些更改会使Java 中的lambda 表达式更类似于其它语言,但是初步讨论表明大家都不同程度地支持这个方案。这个JEP 补充了一系列其他建议来改进Java 语言,包括局部变量类型推断增强的枚举,所有这些改进都可能出现在Java 10 中。

尽管上述三个更改都与lambda 功能有关,但它们之间是相互独立的,其中一些可能会被舍弃,而其他的则取决于反馈情况。因此,我们将在本文中单独解释它们。

更好的消歧

当在Java 8 中添加lambda 功能时,必须修改类型推断以支持它们。然而,过去进行的更改并没有达到预期的效果,部分原因是担心这些更改会使第一次接触lambda 的开发者感到困惑。但是,从这一点来说,情况似乎正在改变,并且如果上下文提供了充足的信息,而编译器无法推断lambda 的类型时,一些开发者会感到沮丧。以下例子说明了lambda 类型推断如何工作:

复制代码
// Case 1: 推断为 Predicate <String> 的 lambda 类型
// 第一个重载方法将被调用
private void m(Predicate<String> ps) { /* ... */ }
private void m(Function<String, String> fss) { /* ... */ }
private void callingM() {
m((String s) -> s.isEmpty());
}
// Case 2: 没有足够的信息来独立地推断 lambda 的类型
// 不过 m2 没有被重载
// 将方法参数的签名应用于 lambda 来推断
// s 是 String 类型并且 s.length() 返回 Integer 类型
private void m2(Function<String, Integer> fsi) { /* ... */ }
private void callingM2() {
m2(s -> s.length());
}
// Case 3: 没有足够的信息来独立地推断 lambda 的类型
// m3 是重载的,但不同的版本有不同的参数数量
// 只有第一个版本和 parameters (1) 的数量匹配
// 将方法参数的签名应用于 lambda 来推断
// s 是 String 类型并且 s.length() 返回 Integer 类型
private void m3(Function<String, Integer> fsi) { /* ... */ }
private void m3(Function<String, Integer> fsi, String s) { /* ... */ }
private void callingM3() {
m3(s -> s.length());
}
// Case 4: 没有足够的信息来独立地推断 lambda 的类型。
// m4 的多个重载版本具有相同数量的可用参数
// Ambiguous call, ERROR
private void m4(Predicate<String> ps) { /* ... */ }
private void m4(Function<String, String> fss) { /* ... */ }
private void callingM4() {
m4(s -> s.isEmpty());
}

然而,对于最后一种情况来说,有足够的信息来判断 m4 的第一个重版本就是被调用的那个,尽管编译器当下并没有使用该信息。根据新建议,编译器可以按照以下步骤来解决歧义:

  1. 这两种可能性都假定 lambda 的参数是一个 _String_,所以 _s_ 可以被认为是 _String_ 类型
  2. 现在我们知道 _s_ 是一个 _String_,我们知道方法 _String.isEmpty()返回 _boolean
  3. 由于 lambda 返回 _boolean_,_m4_ 的第二个变体不匹配,因此它被舍弃
  4. 只剩下一个选项就是 _m4_ 的第一个变体,它与 lambda 的推断类型匹配,因此使用第一个变体

类似的论证也可以应用于方法引用。

用下划线表示未使用的参数

某些情况下,我们希望得到具有多个参数的 lambda 表达式,尽管不会使用所有的参数,但这要求开发者使用指示性名称来表示未使用的参数。这个更改将允许使用下划线来表示未使用的参数。

复制代码
Function<String, Integer> noneByDefault = notUsed -> 0; // 目前
Function<String, Integer> noneByDefault = _ -> 0; // 建议

这是一个其他几种语言都有的功能,如 Scala、Ruby 或 Prolog,但是,在 Java 中这不能轻易地实现,因为直到 Java 7,下划线仍然是一个有效的标识符,它可以在代码其它地方使用。为了引入这种更改而不需要重写大量的代码,这一功能需要逐步完善:

  1. Java 8:当下划线用作标识符时,会发出警告,阻止开发人员使用它 ; 下划线不允许作为 lambda 中的标识符(这不会导致任何向后不兼容问题,因为在 Java 8 之前的版本并没有 lambda 功能)。
  2. Java 9:Java 8 中的警告变为错误,确保将使用下划线作为标识符从 Java 代码语料库中删除。
  3. Java 10(或更高版本):下划线作为标识符重新引入,但仅适用于 lambda 表达式中未使用的参数。

通过初步的讨论来看,对这一更改的意见似乎并不一致 ; 一些用户喜欢新建议的简洁性,但另一些用户喜欢使用明确的名称。在进一步的讨论中可能达成共识。

参数的隐藏

这也许是新提出的功能中最有争议的一个。当前 lambda 参数不允许影响外部变量,意味着参数名称必须不同于当前作用域中可访问的任何变量 ; 这与其他包含作用域保持一致,比如 _while_ 循环或 _if_ 语句:

复制代码
String s = "hello";
if(finished) {
String s = "bye"; // 错误,s 已经定义
}
Predicate<String> ps = s -> s.isEmpty(); // 错误,s 已经定义

如果建议的更改继续下去,lambda 参数将能够重复使用和隐藏现有标识符。在某些情况下,这可能有利于用户,也就是说他们不需要使用其他不太直接的名称作为他们的 lambda 参数名称(上面的例子通常会被重写为 _s2 - > s2.isEmpty()_),但是,就像国际知名演讲者 Roy Van Rijn提出的那样,它也可能引入微小的错误:

复制代码
Map<String, Integer> map = /* ... */
String key = "theInitialKey";
map.computeIfAbsent(key, _ -> {
String key = "theShadowKey"; // shadow variable
return key.length();
});

目前,上述代码是不被允许的,但根据新提案这也可能是对的。如果标记为“shadow variable”的行被删除,代码将仍然可以编译和运行,但它会做完全不同的事情。

为了评估上述更改是否将被引入 Java 以及将以什么形式引入,他们还将进行更多的讨论。然而,毫无疑问的是,在 Java 8 中引入 lambda 只是未来 Java 语言的众多改进的第一步。

查看英文原文: Java 10 Could Bring Upgraded Lambdas


感谢薛命灯对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2017-01-22 18:007728

评论

发布
暂无评论
发现更多内容

阿里云对边缘计算的探索和实践

阿里云CloudImagine

边缘计算

Alluxio入选英特尔AI百佳创新激励计划

Alluxio

人工智能 创新 英特尔 Alluxio 9月月更

07_Linux基础-计划任务-备份脚本-变量定义和使用

mycpen

Linux

08_Linux基础-vim-tmux-字符编码

mycpen

Linux

【大话C语言】分支与循环语句

Albert Edison

C语言 开发语言 for循环 if语句 9月月更

Java编程之语法结构

魏铁锤

Nacos 企业版如何提升读写性能和可观测性

阿里巴巴中间件

阿里云 微服务 云原生 中间件 可观测

图文实录|多模态自然语言处理最新进展

澜舟孟子开源社区

人工智能 自然语言处理 算法 后端

05_Linux基础-NGINX编译安装^判断是否启动^修改端口^启动停止重启^相关路径^中文乱码-Windows、Linux文件传输

mycpen

Linux

03_Linux基础-文件类型-主辅提示符-第1提示符-Linux命令-内外部命令-快捷键-改为英文编码-3个时间-stat-其他基础命令

mycpen

Linux

视频会议,WebRTC及RingCentral解决之道

RingCentral铃盛

WebRTC 视频会议 企业号九月金秋榜

06_Linux基础-NGINX和浏览器、网页的关系-云服务器ssh登陆-安装NGINX-上传网页-压缩命令-xz-gzip-bzip2-zip-tar-配置NGINX服务器支持下载功能-备份脚本

mycpen

Linux

《数据结构》线性表之顺序表的实现(C语言)

孤衫

数据结构 C语言 9月月更

超越所有人的成就,牛顿的光芒也无法掩盖的天才数学巨人

图灵社区

数学 微积分 数学家

卡塔尔世界杯足球赛dapp系统智能合约开发

开发微hkkf5566

MobPush 安卓推送消息数据解析推荐实现

MobTech袤博科技

android 移动推送

【计算讲谈社】第十一讲|商用车智能驾驶商业化实践:“科技+保险”模式探索

大咖说

智能驾驶 商用车

AOP

平凡人生

海泰方圆成功举办“引领数据安全创新,加速数字经济发展”技术研讨会

电子信息发烧客

高端手机市场的诸神之战,vivo举起一把“雷神之锤”

脑极体

09_Linux基础-SHELL-标准输入与标准输出-重定向-管道

mycpen

Linux

设计模式的艺术 第十八章解释器设计模式练习(为数据库备份和同步开发一套简单的数据库同步指令,指令可对数据库中的数据和结构进行备份。例如,输入指令“COPY VIEW FROM srcDB TO desDB”表示将srcDB中所有视图对象复制至desDB)

代廉洁

设计模式的艺术

MobPush 指定页面跳转最佳实现

MobTech袤博科技

ios android intent 页面跳转

Introduction to ByteDance Pitaya

字节跳动终端技术

Python Machine Learning Pitaya ByteDance End Intelligence

如何正确理解Java领域中的锁机制,我们一般需要掌握哪些理论知识?

PivotalCloud

到底什么样的数字化才是企业需要的?用2个数字化案例告诉你

优秀

数字化转型

数据赋能智慧重庆,巴适得很!

云计算

三面阿里,offer到手定级P7,过程很艰辛结果很满意

程序知音

程序员 阿里 java面试 后端技术 Java八股文

超越所有人的成就,牛顿的光芒也无法掩盖的天才数学巨人

图灵教育

数学 微积分 数学家

「技术人生」第9篇:如何设定业务目标

阿里巴巴中间件

阿里云 业务 技术文章

Java 10将带来升级版的Lambda_Java_Abraham Marín Pérez_InfoQ精选文章