写点什么

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:007636

评论

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

【译】日志:每个软件工程师都应该了解实时数据的统一抽象【一】

Rae

架构 分布式 日志 原理

TiFlash 源码解读(七)TiFlash Proxy 模块

PingCAP

TiDB TiDB 源码解读

软件测试 | 测试开发 | app自动化测试(Android)--高级定位技巧

测吧(北京)科技有限公司

xpath

软件测试 | 测试开发 | 简单快速的从GitHub同步代码

测吧(北京)科技有限公司

git

软件测试 | 测试开发 | 用 Pytest+Allure 生成漂亮的 HTML 图形化测试报告

测吧(北京)科技有限公司

pytest Allure

开源治理的基本实践与指导原则

SEAL安全

开源 开源安全 软件供应链安全 开源安全与治理

软件测试 | 测试开发 | AppCrawler 自动遍历测试实践(三):动手实操与常见问题汇总

测吧(北京)科技有限公司

测试

从原理剖析带你理解Stream

华为云开发者联盟

开发 企业号九月金秋榜

软件测试 | 测试开发 | app自动化测试(Android)-- Capability 使用进阶

测吧(北京)科技有限公司

Andriod

软件测试 | 测试开发 | 疫情之下工资翻了2倍多,这4个月学习比工作8年学到的还多

测吧(北京)科技有限公司

软件测试

新书上市 | 十分易上手的图解教程,全系列累计销量20万册!

图灵社区

程序员 计算机 图论

TDengine3.0计算查询引擎的优化与升级

TDengine

数据库 tdengine 时序数据库 企业号九月金秋榜

边缘服务网格 osm-edge 概览

Flomesh

Service Mesh 服务网格

软件测试 | 测试开发 | 测试人生 | 双非学历入职名企大厂还薪资翻倍?

测吧(北京)科技有限公司

面试 测试

上了NVMe的路,才能飙起全闪存的车

脑极体

软件测试 | 测试开发 | 测试人生 | 薪资翻倍涨至50W是种什么样的体验?

测吧(北京)科技有限公司

测试

软件测试 | 测试开发 | Jenkins 集成 Android 代码检查

测吧(北京)科技有限公司

android jenkins

【译】YouTube 架构

Rae

数据库 架构 youtube 后端技术

TiDB v6.2 发版

PingCAP

#TiDB

Python代码用在这些地方,其实1行就够了

华为云开发者联盟

Python 开发 企业号九月金秋榜

易观分析&Pangle联合发布《全球新兴市场移动应用报告》

易观分析

新兴市场

软件测试 | 测试开发 | 如何使用代理配置快速定位接口测试脚本问题?

测吧(北京)科技有限公司

测试

为什么资源隔离对HTAP至关重要?

OceanBase 数据库

开学季 | Y 省教育厅这张卷,融云答出了100分!

融云 RongCloud

通讯协议

你的秋日好运正在派件,请查收9月月更活动!

InfoQ写作社区官方

热门活动 9月月更

华为云快成长直播ERP专场,以数据驱动企业智慧变革

科技云未来

fastposter v2.9.2 最简海报生成器

物有本末

海报生成器 电商海报 图片生成

软件测试 | 测试开发 | 常见接口协议解析

测吧(北京)科技有限公司

TCP

软件测试 | 测试开发 | app自动化测试(Android)--触屏操作自动化

测吧(北京)科技有限公司

自动化测试 app测试

基于MonoRepo的Web端CI/CD实践与优化

RingCentral铃盛

企业号九月金秋榜

助力企业成就好生意,华为云快成长直播

科技云未来

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