Java 10 最重要的 5 个新特性!
局部变量类型推断是有争议的热点,但 Java 10 在 JVM 中的垃圾收集和容器识别上带来了可喜的变化。
关于本系列
所以你认为你了解 Java 编程? 事实是,大多数开发人员只是浮于 Java 平台的表面上,仅仅为了完成工作而学习。在这个正在进行的系列中,Java 技术深入挖掘了 Java 平台的核心功能,提出了一些技巧和诀窍,可以帮助你解决即使是最棘手的编程挑战。
Java™ 开发人员已经习惯了等待新的 Java 版本发布,但是新的、高频率的发布节奏改变了这一情况。Java 9 出现之后仅仅过去 6 个月,现在 Java 10 已经在敲门了。再过 6 个月,我们将迎来 Java 11。一些开发人员可能会发现这样的快速发布是多余的,但是新的节奏标志着一个长期需求的改变。
与它的版本号一样,Java 10 提供了 10 个新特性,本文提供了我认为最重要的 5 个特性(您可以在 Open JDK 10 项目页面上查看它们)。
Java的新版本节奏
从历史上看,JDK 发行的节奏是由大的新特性驱动的。作为最近的例子,Java 8 以 lambda 和流的形式引入了函数式编程,而 Java 9 引入了模块化 Java 系统。每个新版本都被热切地期待着,但是次要的修复程序经常束之高阁,等待更大的组件版本被最终确定。Java 的进化落后于其他语言。
新的高频节奏将 Java 以更小的增量向前推进。在发布日期准备好的特性将被包括在内,而那些不能被安排在下一个版本中,就在 6 个月之后。在这个新周期下的第一个 Java 版本是 Java 9,它于 2017 年 10 月发布。Java 10 于 2018 年 3 月发布,Java 11 将于 2018 年 9 月发布。
作为新节奏的一部分,甲骨文表示,它将只支持每个主要版本,直到下一个主要版本发布为止。 当 Java 11 发布时,Oracle 将停止支持 Java 10。支持 Java 版本的开发人员必须每 6 个月迁移一次主要版本。 不希望或不需要频繁迁移的开发人员可以使用 LTS(长期支持)版本,该版本每三年更新一次。 目前的 LTS 版本 Java 8 将在今年秋季发布 Java 11 之前得到支持。
局部变量类型推断
局部变量类型推断是 Java 10 中最显着的特性。在进入 JDK 10 之前,争论非常激烈,该特性允许编译器推断局部变量的类型,而不是要求程序员明确指定它。
清单 1 显示了如何在 Java 10 之前定义一个String变量类型。
清单 1. 声明并分配一个 String 类型的变量
String name = "Alex"
清单 2 展示了在 Java10 中定义与 String 类型相同的变量
清单 2. 用局部变量类型推断 String 类型的变量
var name = "Alex";
正如你看到的,唯一的区别就是使用了 var 保留类型名称。使用右边的表达式,编译器可以将变量名的类型推断为 String。
这看起来有点简单,让我们来看一个更加复杂的例子。如果一个变量分配给了调用方法的返回值是怎样的?在这种情况下,编译器可以根据方法的返回类型推断变量的类型,如清单 3 所示。
清单 3. 从返回类型推断 String 类型
var name = getName();
String getName(){
return "Alex";
}
使用局部变量类型
顾名思义,局部变量类型推断功能仅适用于局部变量。 它不能用于定义实例或类变量,也不能用于方法参数或返回类型。 但是,您可以在类和增强型循环中使用 var,可以从迭代器中推断出类型,如清单 4 所示。
清单 4. 在循环中使用 var
for(var book : books){}
for(var i = 0; i < 10; i++){}
使用这种类型的最明显的原因是为了减少代码中的冗长。 看看清单 5 中的示例。
清单 5. 很长的类型名称使得代码很长
String message = "Incy wincy spider...";
StringReader reader = new StringReader(message);
StreamTokenizer tokenizer = new StreamTokenizer(reader);
请注意,使用 var 保留类型名称重写清单5时发生了什么。
清单 6. var 类型减少了代码的冗长性
var message = "Incy wincy spider...";
var reader = new StringReader(message);
var tokenizer = new StreamTokenizer(reader);
清单 6 中的类型声明是垂直排列的,并且在构造函数调用的右侧每个申明中都会提到一次类型。 想象一下使用这种类型在一些 Java 框架中常见的长类名的好处。
局部变量类型的问题
1. var 掩盖了类型
你已经看到了 var 如何提高代码的可读性,但是从另一方面来看,它也可以掩盖它。 看看清单7中的示例。
清单 7. 返回类型不清楚
var result = searchService.retrieveTopResult();
在清单 7 中,我们必须猜测返回类型。 让读者猜测发生了什么的代码是难以维护的。
2. var 不能与 lambda 一起使用
与 lambda 表达式一起使用时,类型推断效果不佳,主要原因是编译器缺少类型信息。 清单8中的 lambda 表达式不会被编译。
清单 8. 类型信息不足
Function<String, String> quotify = m -> "'" + message + "'";
var quotify = m -> "'" + message + "'";
在清单 8 中,编译器的右边表达式中没有足够的类型信息来推断变量类型。 Lambda 语句必须始终声明一个显式类型。
3. var 不会与菱形操作符混在一起
与菱形操作符一起使用时,类型推断也不能很好地工作。 看看清单 9 中的例子。
清单 9. 使用带有 var 的菱形运算符
var books = new ArrayList <>();
亲自尝试一下
想要亲自尝试本地变量类型推断,您需要下载 JDK 10 和一个支持它的 IDE。 IntelliJ 的 EAP(Early Access Program)版本具有此支持。 一旦你下载并安装了它,你可以从本文附带的 GitHub 存储库中检出代码开始。 你会在那里找到局部变量类型推断的例子。
在代码清单 9 中,books 的 ArrayList 的参数类型是什么呢?你可能明白你是希望 ArrayList 存储一个书的列表,但是编译器不能推断出来。反之,编译器会做的唯一它能做的事情,就是推断出来这是一个参数是 Object类型ArrayList:ArrayList