JAVA 17:模式匹配子类型

DEV*_*DEV 3 java eclipse java-17

我正在使用 Java 17,最后一行不应编译,但是当我使用 eclipse 时它会编译,现在我很困惑该值应该是子类型还是可以是任何东西?

Double value = 123.3;
if(value instanceof Double ) {}
if(value instanceof Double data) {} // DOES NOT COMPILE
Run Code Online (Sandbox Code Playgroud)

Sil*_*olo 6

该语法是 Java 中相对较新的功能 (JDK 16) 的一部分,他们将其称为模式匹配,并在JEP 394中详细介绍。

如果变量的运行时类型等于其声明的类型,则该示例很简单。所以让我们假设我们有一个

Object value;
Run Code Online (Sandbox Code Playgroud)

其运行时类型恰好是Double.

if (value instanceof Double) {
  ...
}
Run Code Online (Sandbox Code Playgroud)

这个语句里面如果是aif就会在运行时运行。然而,该变量的编译时类型仍然是,这导致了一个非常常见(且冗余)的范例。valueDoubleObject

if (value instanceof Double) {
  Double data = (Double)value; // This cast ALWAYS succeeds
  ...
}
Run Code Online (Sandbox Code Playgroud)

因为在检查类型之后,我们可能希望使用该变量作为该类型。

所以使用 JEP 394,我们可以写

if (value instanceof Double data) {
  ...
}
Run Code Online (Sandbox Code Playgroud)

它一次性完成类型检查和转换。最重要的是,该变量立即绑定,并且在后面的连接子句中可用。

if ((value instanceof Double data) && (data.doubleValue() > 0.0)) {
  ...
}
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为datais a Double,而不是 an Object。与更详细的预模式匹配语法相反

if ((value instanceof Double) && (((Double)value).doubleValue() > 0.0)) {
  ...
}
Run Code Online (Sandbox Code Playgroud)

或者

if (value instanceof Double) {
  Double data = (Double)value;
  if (data.doubleValue() > 0.0) {
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

在您的特定情况下,由于声明的类型和运行时类型都是Double,因此额外的变量不会给您带来任何好处。这是一种退化的情况,模式匹配语法没有用。但它仍然是允许的。根据编译器的不同,您可能会收到有关此的警告。


Tho*_*ger 6

JLS-15.20.2明确指出这是不允许的:

\n
\n

如果 RelationalExpression 的类型是 Pattern 类型的子类型,则会发生编译时错误

\n
\n

关于“亚型”的含义,JLS-4.10规定:

\n
\n

类型的超类型是通过直接超类型关系(写作 )上的自反和传递闭包获得的S >1 T,该关系由本节后面给出的规则定义。我们写S :> T是为了表明 S 和 T 之间存在超类型关系。

\n

S 是T 的真超类型,写作S > T,如果S :> T并且S \xe2\x89\xa0 T

\n

类型 T 的子类型都是类型 U,因此 T 是 U 的超类型和 null 类型。我们写T <: S是为了表明类型 T 和 S 之间存在子类型关系。

\n

T 是S 的真子类型,写作T < S,如果T <: SS \xe2\x89\xa0 T

\n
\n

从措辞中可以清楚地看出,类型T必须是其自身的子类型(和超类型)。如果T不能是其自身的子类型(超类型),则无需定义术语“适当的子类型”和“适当的超类型”

\n
\n

该决定的原因记录在JEP-394中

\n
\n

使模式instanceof表达式将S类型的表达式与T类型的模式进行比较时产生编译时错误,其中S是T的子类型。(此instanceof表达式将始终成功,然后毫无意义。相反的情况,模式匹配总是失败的地方,已经是一个编译时错误。)

\n
\n
\n

请注意(正如 Holger 所指出的)即将推出的 Java 21 版本将取消 RelationalExpression 不能是 Pattern 类型的子类型的要求。这意味着使用 Java 21 您的代码将可以编译。

\n

  • 仅仅因为某件事看起来毫无意义就禁止它,这是一种奇怪的推理。对我来说,与“instanceof”其他用法的一致性看起来更为重要。该测试甚至并不总是会成功,因为如果运行时值为“null”,它将失败。此外,它与模式匹配的进化方式相矛盾。因此,目前的情况是 JDK 21 将放弃这条规则。 (3认同)