instanceof Java 中的模式匹配,而不是编译

instanceof Pattern matching in Java, not compiling

提问人:Flavian Diol D 提问时间:11/9/2023 最后编辑:Flavian Diol D 更新时间:11/15/2023 访问量:251

问:

以下是摘自《Java - The Complete Reference》一书第 12 版的一段话。

Number myOb = Integer.valueOf(9);
int count = 10; 
if((count < 100) && myOb instanceof Integer iObj) { // is OK 
// myOb is both an Integer and nonnegative, and count is less than 100. 
iObj = count; 
//.. 
}

编译此片段是因为 if 块仅在 && 的两端都

真。因此,在 if 块中使用 iObj 是有效的。但是,如果出现以下情况,将导致编译错误

您尝试使用 & 而不是 &&,如下所示:

if((count < 100) & myOb instanceof Integer iObj) { // Error!

在这种情况下,编译器无法知道 iObj 是否在 if 块的范围内

因为 & 的右侧不一定会被评估。

我完全不明白最后几行,如果模式匹配失败,为什么代码甚至会进入 if 块,如果它成功并且第一个条件为真,那么使用“&”而不是“&&”有什么问题。

如果我在 if 块中手动初始化 iObj,该代码有效,但为什么模式匹配不这样做?当它的整个想法是创建一个引用变量时,指向上述类型的提取对象。

手动初始化前我得到什么

Compile Error: iObj may not be initialized

我试过了什么

if((count < 100) & myOb instanceof Integer iObj) {
int iObj = (Integer) myOb;
}

现在它起作用了。

我想知道为什么会发生这种情况。

我在 Windows 中使用 JDK 20

java version "20.0.1" 2023-04-18
Java(TM) SE Runtime Environment (build 20.0.1+9-29)
Java HotSpot(TM) 64-Bit Server VM (build 20.0.1+9-29, mixed mode, sharing)
Java 对象 编译器错误 模式匹配 实例

评论

0赞 markspace 11/9/2023
but why is pattern matching not doing it?它是为您执行初始化的新语法。如果没有可用或未启用新功能,则无法编译和运行该代码。
0赞 Flavian Diol D 11/9/2023
我使用 JDK 17,它具有该功能,我相信它与启用或禁用无关。这是关于使用“&”或“&&”。我希望你试试代码,如果你发现什么,请告诉我。谢谢。
0赞 Lii 11/17/2023
你引用的博克的作者赫伯特·希尔德(Herbert Schildt)显然在编写各种语言的编程书籍方面享有盛誉,这些书籍充满了错误。例如,关于他在 90 年代的 C 书,有人这样说:“描述一种语言微妙但绝对不同于 C”。你的报价似乎证实了这一点。它不仅是错误的,而且格式很糟糕,并且无法使用自动装箱,这在语言中已经存在了 20 年。&

答:

0赞 tquadrat 11/9/2023 #1

首先,问题中提出的报价是废话!

是“捷径”形式,不保证双方都会被执行;在这种情况下,如果第一项的计算结果为 ,则永远不会触及第二项。 保证对这两个术语进行评估。&&false&

然而,由于某种原因,模式匹配可能不适用于单身(但不适用于引文中给出的那个!!&

在 Java 14 中作为预览引入了模式匹配,在 Java 15 中开始了第二个预览,直到在 Java 16 中完成。事实上,JEP 只谈论 和 ,而不是 和 。但是,让它的行为与快捷方式不同是没有多大意义的。instanceof&&||&|

同时,我尝试了

if( (count < 100) & myOb instanceof Integer iObj ) 
{
   int iObj = (Integer) myOb;
}

使用 Java 21,它出乎意料地有效!但是当我使用 时,它会抱怨正文中的赋值/声明,因为已经声明了......奇怪!!&&iObj

但是 JLS 6.3.1 也只谈论快捷方式......

也许 JLS 15.22.2 给出了答案:There 和 被标记为“布尔逻辑运算符”,而 6.3.1 则分别将 和 标记为“Conditional-And”或“Conditional-Or”运算符。&|&&||

但随之而来的问题是,为什么术语中与 single 的悬空不会导致语法错误?iObjinstanceof&

评论

1赞 Lii 11/11/2023
但在 JLS 6.3.1 中没有提到。您仍然可以将模式与 一起使用,但模式变量不会传播到 if-body。您只能在 if-test-expression 中使用 pattern 变量。因此,可以重用该名称来声明正文中的另一个变量。你觉得怎么样?&&iObjiObj
5赞 user85421 11/11/2023
JLS 6.3.1 - “表达式中模式变量的作用域” - 声明没有作用域规则适用于(与 和相反) - 因此,在 中,子表达式是允许的,并引入了一个模式变量 (PV),但由于没有 的作用域规则,因此不会编译(“找不到符号”)&&&instanceofa instanceof B aB & aB.isValid()a instanceof B aB&aB.isValid()
1赞 Lii 11/12/2023
@tquadrat我现在试过了。user85421 是对的:它不起作用,我错了。这似乎是允许的,但与.a instanceof B b&
1赞 Flavian Diol D 11/12/2023
@user85421介意将其作为带有示例和详细解释的答案发布吗?将是一个很大的帮助。谢谢
1赞 Lii 11/13/2023
评论线程不适合长时间的讨论,但我喜欢这个...... :) @davidalayachew “绝对不是没用的” 我认为没用的是将 ,并与声明变量的模式结合。这个变量似乎根本没有意义,因为它没有传播到任何地方,所以你不能使用它。逻辑本身有时很有用,它声明一个变量非常有用,但在此示例中不是组合。还是我错过了什么?a instanceof B binstanceof&&instanceof&instanceof
2赞 Kroppeb 11/15/2023 #2

引文解释为什么第二种情况没有编译的方式措辞非常奇怪,甚至可能是错误的。所以这是我的解释方式。

查看模式匹配变量的方式是,只有在赋值时才会声明它们(在规范中,他们称之为“引入”模式变量)。

让我们看一个例子:

if(myOb instanceof Integer iObj) {
    // ...
}

现在在这个 if 语句中,必须返回了 ,所以被“引入”并因此声明。这意味着你既可以从中读取,也可以写入它(因为它不是最终变量,请注意这是有效的 Java)。instanceoftrueiObjmyOb instanceof final Integer iObj

现在让我们跳到给出的第一个案例:

if((count < 100) && myOb instanceof Integer iObj) {
    // ...
}

那么为什么会这样呢?语言设计者确保,如果您有以下形式的语句:

if (exprA && exprB) {
  // ...
}

它等同于:

if (exprA) {
  if (exprB) {
    // ...
  }
}

这就是为什么你在 Java 和许多其他语言中短路的原因。 对第一种情况这样做,我们得到:

if (count < 100) {
  if (myOb instanceof Integer iObj) {
    // ...
  }
}

因为内部 if 与我给出的第一个示例匹配,所以在 if 块中,模式变量必须被“引入”并且因此是可分配的,这是有道理的。

那么为什么这不能像 一样工作呢?好吧,因为语言设计者决定不这样做。&&&

关于他们为什么决定这样做,我确实有一个论点。给出这样的语句:

if (exprA & exprB) {
  // ...
}

它本质上与以下相同:

boolean tempA = exprA;
boolean tempB = exprB;
if (tempA && tempB) {
  // ...
}

虽然我们可以在这里再次拆分运算符,但这并不重要。如果你给,我们会得到. 此语句不能“引入”模式变量,因为它可能不是整数,这意味着它不能保证会被赋值。&&boolean tempB = myOb instanceof Integer iObjmyObiObj

因此,即使 'myOb instanceof Integer iObj' 在这两种情况下都必须返回 true 才能运行 if 块中的代码,因为语言设计者没有像他们那样制定如何引入模式变量的规则,本书中的第 2 种情况不会像第一种情况那样编译。&&&

那么为什么你可以在if块中使用with而不是with呢? 好吧,正如我们刚才看到的,编译器不认为模式变量被“引入”到块中,所以你可以在里面定义它,但这是非法的,因为模式变量是引入的,这意味着你不能重新声明它。int iObj = ...&&&&&&

评论

0赞 Flavian Diol D 11/16/2023
所以基本上是因为他们没有完善这个功能,而 Java 中的设计对于模式匹配来说是不完整的?&
1赞 Kroppeb 11/16/2023
@FlavianDiolD我认为这是他们做出的设计决定。模式引入的工作方式与确定赋值的工作方式非常相似(Java 规范的第 16 章)。规范的这一部分也没有任何特殊的外壳,就像他们所拥有的那样。但这只是把问题变成了“为什么在确定的赋值分析中没有特殊的大小写?”,我没有答案。&&&&
0赞 Flavian Diol D 11/16/2023
好的,非常感谢@Kroppeb