JEP 375:instanceof的模式匹配(第2版预览)

官方原文(英文)地址: https://openjdk.java.net/jeps/375
个人原创翻译,转载请注明出处。

Summary

Enhance the Java programming language with pattern matching for the instanceof operator. Pattern matching allows common logic in a program, namely the conditional extraction of components from objects, to be expressed more concisely and safely. This is a preview language feature in JDK 15.

摘要

instanceof运算符的模式匹配来增强Java编程语言。模式匹配使程序中的通用逻辑,即从对象中有条件地提取组件,得以更简洁、更安全地表示。这是JDK 15的一个预览语言特性

History

Pattern matching for instanceof was proposed by JEP 305 in mid 2017, and targeted to JDK 14 in late 2019 as a preview language feature. This JEP proposes to re-preview the feature in JDK 15, with no changes relative to the preview in JDK 14, in order to gather additional feedback.

历史

instanceof的模式匹配在2017年中期由JEP 305提出,并在2019年末在JDK 14中作为预览语言特性。本JEP提出在JDK 15中再次预览该特性,相对于JDK 14中的预览没有变化,以便收集附加反馈。

Motivation

Nearly every program includes some sort of logic that combines testing if an expression has a certain type or structure, and then conditionally extracting components of its state for further processing. For example, all Java programmers are familiar with the instanceof-and-cast idiom:

动机

几乎每个程序都包含某种逻辑,结合了对表达式的类型或结构的测试,然后有条件地提取其中的状态组件以进行进一步处理。例如,所有Java程序员都熟悉“先instanceof再转换”的习惯用法:

if (obj instanceof String) {
    String s = (String) obj;
    // use s
}
if (obj instanceof String) {
    String s = (String) obj;
    // 使用s
}

There are three things going on here: a test (is obj a String?), a conversion (casting obj to String), and the declaration of a new local variable (s) so we can use the string value. This pattern is straightforward and understood by all Java programmers, but is suboptimal for several reasons. It is tedious; doing both the type test and cast should be unnecessary (what else would you do after an instanceof test?). This boilerplate -- in particular, the three occurrences of the type String --- obfuscates the more significant logic that follows. But most importantly, the repetition provides opportunities for errors to creep unnoticed into programs.

这里发生了三件事情:一个测试(obj是不是一个String),一个转换(将obj转换为String),和定义一个新的局部变量(s)以便我们可以使用字符串的值。这种模式很简单,并且所有Java程序员都可以理解,但是由于一些原因,这不是最优的。这很乏味:应该没有必要既做类型测试,同时又做类型转换(你还能在instanceof测试之后做什么其他的呢?)。这些样板代码——特别是出现了三次的String类型——混淆了后面更重要的逻辑。但最重要的是,重复代码为错误提供了机会且不易被察觉。

Rather than reach for ad-hoc solutions, we believe it is time for Java to embrace pattern matching. Pattern matching allows the desired 'shape' of an object to be expressed concisely (the pattern), and for various statements and expressions to test that 'shape' against their input (the matching). Many languages, from Haskell to C#, have embraced pattern matching for its brevity and safety.

与寻求特定的解决方案相比,我们相信是时候让Java拥抱模式匹配了。模式匹配允许简洁地表达对象所需的“形态”(模式),并允许各种语句和表达式针对其输入来测试“形态”(匹配)。从Hashkell到C#,许多语言都出于其简洁性和安全性而拥抱了模式匹配。

Description

描述

A pattern is a combination of (1) a predicate that can be applied to a target, and (2) a set of binding variables that are extracted from the target only if the predicate successfully applies to it.

模式是二者的组合:(1)可以应用于目标的谓词;(2)仅在谓词成功应用于目标时才从目标中提取的一组绑定变量

A type test pattern consists of a predicate that specifies a type, along with a single binding variable.

类型测试模式由指定类型的谓词和单个绑定变量组合。

The instanceof operator (JLS 15.20.2) is extended to take a type test pattern instead of just a type. In the code below, the phrase String s is the type test pattern:

扩展instanceof运算符(JLS 15.20.2)以采用类型测试模式,而不仅仅是类型。在下面的代码中,短语String s就是类型测试模式:

if (obj instanceof String s) {
    // can use s here
} else {
    // can't use s here
}
if (obj instanceof String s) {
    // 这里可以使用s
} else {
    // 这里不能使用s
}

The instanceof operator "matches" the target obj to the type test pattern as follows: if obj is an instance of String, then it is cast to String and assigned to the binding variable s. The binding variable is in scope in the true block of the if statement, and not in the false block of the if statement.

instanceof运算符将目标obj与类型测试模式相“匹配”:如果objString的实例,那就将其转换为String并赋值给绑定变量s。绑定变量的作用域是if语句的true代码块,而不是if语句的false代码块。

The scope of a binding variable, unlike the scope of a local variable, is determined by the semantics of the containing expressions and statements. For example, in this code:

与局部变量的作用域不同,绑定变量的作用域由包含的表达式和语句的语义确定。例如,在这样的代码中:

if (!(obj instanceof String s)) {
    .. s.contains(..) ..
} else {
    .. s.contains(..) ..
}
if (!(obj instanceof String s)) {
    .. s.contains(..) ..
} else {
    .. s.contains(..) ..
}

the s in the true block refers to a field in the enclosing class, and the s in the false block refers to the binding variable introduced by the instanceof operator.

true代码块中的s引用封闭类中的字段,false代码块中的s引用由instanceof运算符引入的绑定变量。

When the conditional of the if statement grows more complicated than a single instanceof, the scope of the binding variable grows accordingly. For example, in this code:

if语句的条件变得比单个instanceof更复杂时,绑定变量的范围也会相应地增长。例如,在这样的代码中:

    if (obj instanceof String s && s.length() > 5) {.. s.contains(..) ..}
    if (obj instanceof String s && s.length() > 5) {.. s.contains(..) ..}

the binding variable s is in scope on the right hand side of the && operator, as well as in the true block. (The right hand side is only evaluated if instanceof succeeded and assigned to s.) On the other hand, in this code:

绑定变量s的作用域既是&&运算符的右边范围,同时也是true代码块范围。(右边范围只会在instanceof成功时运算并赋值给s。)另外,在这样的代码中:

    if (obj instanceof String s || s.length() > 5) {.. s.contains(..) ..}
    if (obj instanceof String s || s.length() > 5) {.. s.contains(..) ..}

the binding variable s is not in scope on the right hand side of the || operator, nor is it in scope in the true block. (s at these points refers to a field in the enclosing class.)

绑定变量s的作用域既不是code>||运算符的右边范围,同时也不是true代码块范围。(此例中的s引用封闭类型中的字段。)

There are no changes to how instanceof works when the target is null. That is, the pattern will only match, and s will only be assigned, if obj is not null.

当目标是null时instanceof的工作方式不变。即,只有obj不是null时,模式才会匹配,s才会被赋值。

The use of pattern matching in instanceof should dramatically reduce the overall number of explicit casts in Java programs. Moreover, type test patterns are particularly useful when writing equality methods. Consider the following equality method taken from Item 10 of the Effective Java book:

instanceof中使用模式匹配应该会大大减少Java中显式强制转换的总数。此外,类型测试模式在编写相等方法时特别有用。考虑下面选择Effective Java一书第10条的相等方法:

@Override public boolean equals(Object o) { 
    return (o instanceof CaseInsensitiveString) && 
        ((CaseInsensitiveString) o).s.equalsIgnoreCase(s); 
}
@Override public boolean equals(Object o) { 
    return (o instanceof CaseInsensitiveString) && 
        ((CaseInsensitiveString) o).s.equalsIgnoreCase(s); 
}

Using a type test pattern means it can be rewritten to the clearer:

使用类型测试模式意味着这可以重新写成更清楚的样子:

@Override public boolean equals(Object o) { 
    return (o instanceof CaseInsensitiveString cis) && 
        cis.s.equalsIgnoreCase(s); 
}
@Override public boolean equals(Object o) { 
    return (o instanceof CaseInsensitiveString cis) && 
        cis.s.equalsIgnoreCase(s); 
}

The instanceof grammar is extended accordingly:

instanceof语法被相应地扩展:

RelationalExpression:
     ...
     RelationalExpression instanceof ReferenceType
     RelationalExpression instanceof Pattern

关系表达式:
     ...
     关系表达式 instanceof 引用类型
     关系表达式 instanceof 模式

Future Work

Future JEPs will enhance the Java programming language with pattern matching for other language constructs, such as switch expressions and statements.

未来的工作

未来的JEP将通过与其他语言结构,如switch表达式和语句,进行模式匹配来增强Java编程语言。

Alternatives

The benefits of type-test patterns could be obtained by flow typing in if statements, or by a type switch construct. Pattern matching generalizes both of these constructs.

替代方案

类型测试模式的好处可以通过在if语句的流式键入类型switch结构中获得。模式匹配涵盖了这两种结构。

Dependencies

The implementation may make use of JEP 309 (Dynamic Class-File Constants).

依赖

实现可能用到JEP 309(动态Class文件常量)