【AICon】AI 基础设施、LLM运维、大模型训练与推理,一场会议,全方位涵盖! >>> 了解详情
写点什么

自 Java 8 以来的新语言特性

  • 2019-08-16
  • 本文字数:4023 字

    阅读完需:约 13 分钟

自Java 8以来的新语言特性


本文介绍了自 Java 8 以来与 Java 语言相关的改进。密切关注 Java 平台是很重要的,因为按照新的快速发布节奏,每六个月就会发布一个新的 Java 版本对平台和语言进行更改。


当 Java 8 引入 StreamsLambdas 时,那是一个巨大的变化,使得函数式编程风格可以用更少的样板代码来表达。虽然最近的版本没有添加这样有影响的特性,但是也对该语言进行了许多较小的改进。


本文总结了 Java 8 之后发布的 Java 版本中包含的语言增强。有关所有塑造新平台的 JEP 的概述,请查看这篇博文。

局部变量类型推断

自 Java 8 以来,最显著的语言改进可能是添加了关键词 var。它最初是在 Java 10 中引入的,并在 Java 11 中得到了进一步的改进。


该特性允许我们通过省略显式类型说明来简化局部变量声明的过程:


var greetingMessage = "Hello!";
复制代码


虽然它看起来类似于 JavaScript 的 var 关键字,但这与动态类型无关。


引用 JEP 的一句话:


我们寻求通过减少与编写 Java 代码相关的仪式来改进开发人员的体验,同时保持 Java 对静态类型安全性的承诺。


声明变量的类型在编译时进行推断。在上面的示例中,推断出的类型是 String。使用 var 而不是显式类型可以减少这段代码的冗余,因此更容易阅读。


下面是另一个很适合类型推断的场景:


MyAwesomeClass awesome = new MyAwesomeClass();
复制代码


很明显,在很多情况下,这个特性可以提高代码质量。然而,有时坚持使用显式类型声明会更好。让我们看几个用 var 替换类型声明可能适得其反的例子。

注意可读性

第一种情况是,从源代码中删除显式类型信息会降低可读性。


当然,IDE 可以在这方面提供帮助,但是在代码评审期间,或者在快速扫描代码时,它可能会破坏可读性。例如,考虑下工厂或构建器:你必须找到负责对象初始化的代码来确定类型。


这里有个小谜题。下面这段代码使用 Java 8 的日期 / 时间 API。猜猜下面代码片段中变量的类型:


var date = LocalDate.parse("2019-08-13");var dayOfWeek = date.getDayOfWeek();var dayOfMonth = date.getDayOfMonth();
复制代码


好了吗?答案是这样的:


第一个非常直观,parse 方法返回一个 LocalDate 对象。但是,对于接下来的两个方法,你需要更熟悉这些 API:dayOfWeek 返回 java.time.DayOfWeek,而 dayOfMonth 只返回一个 int。


另一个潜在的问题是,使用 var,读者不得不更多地依赖上下文。考虑以下代码片段:


private void horriblyLongMethod() {    // ...    // ...    // ...
var dayOfWeek = date.getDayOfWeek();
// ... // ... // ...}
复制代码


根据前面的例子,我敢打赌你一定会猜到它是 java.time.DayOfWeek 类型。但这次,它是一个整数,因为本例中的日期来自 Joda time。它是一个不同的 API,行为略有不同,但你看不到它,因为它是一个比较长的方法,而且你没有阅读所有的代码行。(JavaDoc:Joda time/Java 8 Date/ Time API)


如果显式类型声明存在,那么弄清楚 dayOfWeek 的类型就很简单了。现在,使用 var,读者首先必须找出 date 变量的类型并检查 getDayOfWeek 做了什么。这在 IDE 中很简单,但在扫描代码时就不那么简单了。

注意保留重要的类型信息

第二种情况是,当使用 var 删除所有可用的类型信息时,甚至都无法推断出来。在大多数情况下,这些情况是由 Java 编译器捕获的。例如,var 不能为 Lambda 表达式或方法引用推断类型,因为对于这些特性,编译器依赖于左侧表达式来推断类型。


然而,也有一些例外。例如,var 不能很好地处理 Diamond 操作符。Diamond 操作符是一个很好的特性,可以在创建泛型实例时消除表达式右侧的一些冗余:


Map<String, String> myMap = new HashMap<String, String>(); // 在Java 7之前Map<String, String> myMap = new HashMap<>(); // 使用Diamond操作符
复制代码


因为它只处理泛型类型,所以仍然有冗余需要删除。让我们试着用 var 使它更简洁:


var myMap = new HashMap<>();
复制代码


这个例子是有效的,Java 11 甚至没有在编译器中发出关于它的警告。然而,使用所有这些类型推断,我们最终根本没有指定泛型类型,类型将是 Map<Object, Object>。


当然,这可以通过删除 Diamond 操作符轻松解决:


var myMap = new HashMap<String, String>();
复制代码


当 var 与原始数据类型一起使用时,可能会出现另一组问题:


byte   b = 1;short  s = 1;int    i = 1;long   l = 1;float  f = 1;double d = 1;
复制代码


如果没有显式的类型声明,所有这些变量的类型将被推断为 int。在处理基本数据类型时,使用字面类型(例如 1L),或者在这种情况中根本不使用 var。

请务必阅读官方风格指南

最终由你决定何时使用类型推断,并确保它不会损害可读性和正确性。作为一个经验法则,坚持良好的编程实践,比如良好的命名和最小化局部变量的作用域。务必要阅读官方风格指南和 FAQ 中关于 var 的部分。


因为 var 有太多的陷阱,所以它的引入比较保守,并且只能用于局部变量,而局部变量的作用域通常非常有限。


此外,它被谨慎地引入,var 不是一个新的关键字,而是一个保留类型名。这意味着只有当它作为类型名使用时,它才具有特殊的意义,在其他任何地方,var 都将继续作为有效标识符。


目前,var 没有一个不可变对应项(如 val 或 const)来声明一个最终变量,并用一个关键字来推断它的类型。我们希望将来的版本,在那之前,我们可以使用 final var。


相关资源:


来自 Milling Project Coin 的各种改进

Coin 项目(JSR 334)是 JDK 7 的一部分,它带来了一些方便的语言改进:


  • Diamond 操作符

  • Try-with-resources 语句

  • 多异常捕获和更准确地异常重抛

  • 将 String 用于 switch 语句

  • 二进制整数字面值和数字字面值中的下划线

  • 简化的 Varargs 方法调用


Java 9 继续沿着这条道路前进,并添加了一些更小的改进。

允许在接口中声明私有方法

因为 Java 8 可以向接口添加默认方法。在 Java 9 中,这些默认方法甚至可以调用私有方法来共享代码,从而在需要时重用,但又不想公开暴露功能。


虽然这不是一个大问题,但它是一个逻辑上的补充,让你可以在默认方法中整理代码。

匿名内部类中的 Diamond 操作符

Java 7 引入了 Diamond 操作符(<>),通过让编译器推断构造函数的参数类型来简化代码:


List<Integer> numbers = new ArrayList<>();
复制代码


但是,这个特性以前不能用于匿名内部类。根据项目邮件列表的讨论,这不是作为原始 Diamond 操作符特性的一部分添加的,因为它需要大量的 JVM 更改。


在 Java 9 中,这个小瑕疵得到了完善,使得该操作符更加通用:


List<Integer> numbers = new ArrayList<>() {    // ...}
复制代码

允许将有效 final 变量作为 try-with-resources 语句的资源

Java 7 引入的另一个增强是 try-with-resources,它使开发人员不必担心资源的释放。


为了说明它的强大功能,首先考虑下在 Java 7 之前,下面这个典型的例子为正确关闭资源所做的工作:


BufferedReader br = new BufferedReader(...);try {    return br.readLine();} finally {    if (br != null) {        br.close();    }}
复制代码


借助 try-with-resources,资源可以自动释放,大大减少了仪式代码(ceremony):


try (BufferedReader br = new BufferedReader(...)) {    return br.readLine();}
复制代码


尽管具有强大的功能,但是 try-with-resources 有一些缺点在 Java 9 中才得以解决。


尽管这个构造可以处理多个资源,但它很容易使代码更难阅读。与通常的 Java 代码相比,在 try 关键字之后在列表中声明这样的变量有点非常规:


try (BufferedReader br1 = new BufferedReader(...);    BufferedReader br2 = new BufferedReader(...)) {    System.out.println(br1.readLine() + br2.readLine());}
复制代码


同样,在 Java 7 版本中,如果已经有一个你想要处理的变量采用了这个构造,就必须引入一个虚拟变量。(示例参见 JDK-8068948)。


为了减少批评,除了新创建的变量外,经过增强的 try-with-resources 可以处理 final 或有效 final 局部变量:


BufferedReader br1 = new BufferedReader(...);BufferedReader br2 = new BufferedReader(...);try (br1; br2) {    System.out.println(br1.readLine() + br2.readLine());}
复制代码


在本例中,变量的初始化与它们在 try-with-resources 构造的注册分离。


需要注意的是,现在可以引用已经由 try-with-resources 释放的变量,这在大多数情况下会失败:


BufferedReader br = new BufferedReader(...);try (br) {    System.out.println(br.readLine());}br.readLine(); // Boom!
复制代码


下划线不再是有效的标识符名称 在 Java 8 中,当使用“_”作为标识符时,编译器会发出警告。Java 9 更进一步,使单个下划线字符作为标识符非法,并保留这个名称以便将来用于特殊的语义:


int _ = 10; // Compile error
复制代码

改进警告信息

最后,让我们简单介绍一下与最新 Java 版本中与编译器警告相关的更改。


现在可以使用 @SafeVarargs 注解一个私有方法来标记“Type safety: Potential heap pollution via varargs parameter”警告为假阳性。(事实上,此更改是前面讨论的 JEP 213:Milling Coin 项目的一部分)。要了解更多关于 Varargs、泛型以及组合这些特性可能出现的潜在问题的信息,请阅读官方文档。


同样,从 Java 9 开始,当导入不推荐使用的类型时,编译器不会针对 import 语句发出警告。由于在实际使用不推荐使用的成员时总是会显示单独的警告,所以这些警告没有提供足够的信息,而且是多余的。

总结

本文介绍了自 Java 8 以来与 Java 语言相关的改进。密切关注 Java 平台是很重要的,因为按照新的快速发布节奏,每六个月就会发布一个新的 Java 版本对平台和语言进行更改。


关于作者 David 是一名全堆栈开发人员,具有 10 多年的 Java 和 Web 技术经验。长期以来,他一直提倡开源和测试自动化。他拥有硕士学位,多年来参加了无数的在线课程。你还可以从 Scott Maven 插件和 Vim 插件 Disapprove Deep Indentation 中找到 David 的名字。

原文链接:

https://advancedweb.hu/2019/08/08/post_java_8_language_features/


2019-08-16 17:035445
用户头像

发布了 693 篇内容, 共 402.0 次阅读, 收获喜欢 1502 次。

关注

评论 1 条评论

发布
用户头像
技术更新越来越少了!技术更新时间越来越快了。
2019-08-16 17:49
回复
没有更多了
发现更多内容

服务器安全防护措施有哪些?

Geek_f19a80

服务器

语言忠诚?离不开舒适圈?为什么程序员不喜欢更换编程语言?

代码生成器研究

SketchUp Pro(草图大师 2023)for Mac 支持M1/M2

彩云

SketchUp Pro 2023 犀牛3D建模软件

Infuse Pro for Mac(专业的视频播放器)v7.6.4激活版

影影绰绰一往直前

Kmesh内核级流量治理,服务转发性能提升50%+

华为云开发者联盟

云原生 后端 华为云 华为云开发者联盟 DTSE Tech Talk

零代码AppLink平台基础组件

RestCloud

APPlink

机器学习与 S3 相集成 :释放数据的力量

亚马逊云科技 (Amazon Web Services)

机器学习 S3 云存储服务

Axure RP 10 for Mac(交互式原型设计)v10.0.0.3912官方汉化版

影影绰绰一往直前

得物App安卓冷启动优化-Application篇

得物技术

#运维

Keka for Mac(压缩解压工具) 1.3.6中文版

展初云

Mac 解压缩软件 Keka

如何查看数据库正在执行的SQL?

NineData

数据库 sql SQL优化 SQL开发工具 阻塞SQL

SVN管理工具:Cornerstone 4 for Mac激活版 支持M1

彩云

Cornerstone 4 cornerstone 4下载

亚马逊云科技基于 Polygon 推出首款 Amazon Managed Blockchain Access,助 Web3 开发人员降低区块链节点运行成本

亚马逊云科技 (Amazon Web Services)

re:Invent Amazon CloudFront

“抓机遇,促发展”2024亚洲国际人工智能展览会(世亚智博会)

AIOTE智博会

智博会 上海智博会 人工智能展 世亚智博会 北京智博会

Native Instruments Kontakt 7 for Mac(强大音频采样器)v7.7.2激活版

影影绰绰一往直前

基于阿里云物联网平台的光伏电站智能运维方案

阿里云AIoT

物联网 光伏发电 阿里云; 光伏产业

360°全方位体验华为云开放技术能力——华为云开发者日苏州站成功举办

华为云开发者联盟

华为云 华为云开发者联盟 华为云开发者日

Nginx配置限流

拾光师

2023年大数据场景智能运维实践总结

阿里云大数据AI技术

大数据

Camtasia 2023 for Mac(视频录制和剪辑软件) 2023.3.5中文版

mac

苹果mac Windows软件 屏幕录制软件 视频编辑软件 Camtasia 2023

Dropzone 4 for Mac(文件拖拽操作增强工具)

展初云

效率工具 Mac Dropzone 4

Royal TSX 6 for Mac远程管理软件

展初云

Mac软件 远程桌面管理

Downie 4最新版下载 Downie v4.6.34中文版

影影绰绰一往直前

INFINI Console 与华为鲲鹏完成产品兼容互认证

极限实验室

console 极限科技 华为鲲鹏认证

语言模型文本处理基石:Tokenizer简明概述

Baihai IDP

人工智能 自然语言处理 程序员 AI 白海科技

数据库系统概述之国产数据库

小齐写代码

2024年1月1日起,你家的护眼台灯可能要换新!

电子信息发烧客

3天面了20个候选人,聊聊我的感受

冰河

程序员 面试 系统架构 架构师 技术提升

Infuse Pro for Mac(多媒体播放器)v7.6.4激活版

影影绰绰一往直前

国产数据库来了

小魏写代码

神奇植物在哪里?文心大模型助力一秒读懂花草的“前世今生”

飞桨PaddlePaddle

数据库 大模型 文心大模型 风景园林

自Java 8以来的新语言特性_语言 & 开发_Dávid Csákvári_InfoQ精选文章