[关闭]
@levinzhang 2023-06-18T10:48:34.000000Z 字数 2379 阅读 389

JEP 443:未命名模式和变量致力于提升Java代码的可读性

by

摘要:

JEP 443,未命名模式和变量(预览),已经从JDK 21的Targeted状态变更为Completed状态。这个预览JEP建议“用未命名的模式和未命名变量来增强语言,前者与记录组件相匹配,但无需说明组件的名称和类型,后者可以被初始化但不使用”。


JEP 443,未命名模式和变量(预览),已经从JDK 21的Targeted状态变更为Completed状态。这个预览JEP建议“用未命名的模式和未命名变量来增强语言,前者与记录组件相匹配,但无需说明组件的名称和类型,后者可以被初始化但不使用”。 这两者均由下划线字符表示,如r instanceof _(int x, int y)r instanceof _。这是一个预览的语言特性

未命名模式是为了简化数据处理,尤其是在处理记录类时。它们允许开发人员在模式匹配中省略记录组件的类型和名称,这可以显著提升代码的可读性。例如,考虑如下的代码片段:

  1. if (r instanceof ColoredPoint(Point p, Color c)) {
  2. // ...
  3. }

在这个样例中,如果在if代码块中不需要Color c组件,那么将其包含在模式中可能会很麻烦,而且不够清晰。有了JEP 443,开发人员可以简单地省略不必要的组件,从而得到更干净、更易读的代码:

  1. if (r instanceof ColoredPoint(Point p, _)) {
  2. // ...
  3. }

未命名变量在必须声明变量但是不使用它的值的情况下非常有用。这在循环、try-with-resources语句、catch代码块和lambda表达式中很常见。例如,考虑下面的循环:

  1. for (Order order : orders) {
  2. if (total -> limit) total++;
  3. }

在本例中,order变量在循环中没有被用到。借助JEP 443,开发人员可以使用下划线替换未使用的变量,使代码更加简洁明了:

  1. for (_ : orders) {
  2. if (total -> limit) total++;
  3. }

未命名模式和变量是一个预览特性,默认是禁用的。要使用它,开发人员必须启用预览特性来编译代码,如下面的命令所示:

  1. javac --release 21 --enable-preview Main.java

运行该程序也需要相同的标记:


java --enable-preview Main

但是,我们可以使用源码启动器(source code launcher)来直接运行它。在这种情况下,命令行如下所示:

  1. java --source 21 --enable-preview Main.java

使用jshell方案也是可以的,不过依然需要启用预览特性:

  1. jshell --enable-preview

我们来看看JEP 443介绍的关于未命名模式和变量的几个更高级的使用场景:

未命名模式在嵌套模式匹配的场景下特别有用,在这种情况下,一个记录类中只有某些组件是必需的。例如,考虑一个记录类ColoredPoint,它包含一个Point和一个Color。如果我们只需要Pointx坐标,那么可以使用未命名模式来省略yColor组件:

  1. if (r instanceof ColoredPoint(Point(int x, _), _)) {
  2. // ...
  3. }

未命名模式变量在switch语句中非常有用,在这种情况下,相同的行为会在多种场景下运行,而变量不会被使用。例如:

  1. switch (b) {
  2. case Box(RedBall _), Box(BlueBall _) -> processBox(b);
  3. case Box(GreenBall _) -> stopProcessing();
  4. case Box(_) -> pickAnotherBox();
  5. }

在本例中,前两个场景使用了未命名的模式变量,因为它们的右侧没有使用box组件。第三个场景使用了未命名模式,将box与null组件进行匹配。

未命名变量可以在参数无关紧要的lambda表达式中使用。例如,在下面的代码中,lambda参数v没有被用到,所以它的名字无关紧要:

  1. stream.collect(Collectors.toMap(String::toUpperCase, _ -> "No Data"));

在try-with-resources语句中,资源代表了try块的代码执行的上下文。如果代码不直接使用上下文,资源变量的名称就无关紧要。比如:

  1. try (var _ = ScopedContext.acquire()) {
  2. // No use of acquired resource
  3. }

未命名的变量可以在catch块中使用,其中异常参数的名称是无关紧要的。比如:

  1. try {
  2. int i = Integer.parseInt(s);
  3. } catch (NumberFormatException _) {
  4. System.out.println("Bad number: " + s);
  5. }

值得注意的是,在Java 10中,下划线以前可以作为有效的标识符。但是,从Java 8开始,就不建议使用下划线作为标识符了,在Java 9中,下划线变成了一个编译时的错误。因此,我们认为,在现有的和积极维护的代码中,只有极少数会使用下划线作为变量名。如果确实存在这样的代码,就需要对其进行修改,避免使用下划线作为变量名。

鉴于此,JEP 443朝着使Java代码更可读、更可维护迈出的重要一步。这在复杂的数据结构中尤为有益,因为结构的形状与结构中的数据项同等重要。通过允许开发人员省略不必要的组件和变量,它减少而来代码的混乱,使代码更易于理解。随着开发人员对这一特性获得更多的经验,预计它将成为Java编程的组成部分。

查看英文原文:Streamlining Java with JEP 443: Ushering in a New Era of Java Code Readability

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注