【问题标题】:Why doesn't instanceof pattern matching work with else if in this particular case?为什么在这种特殊情况下 instanceof 模式匹配不能与 else 一起使用?
【发布时间】:2022-06-25 18:26:14
【问题描述】:

以下 sn-p 无法在 javac 版本 17 (Temurin) 上编译

class Instanceof {
    static void doesNotWork(Object o) {
        if (o == null) {
            throw new Error();
        } else if (!(o instanceof String s)) {
            throw new Error();
        }   
        System.out.println(s); // error here
    }
}

它会产生这个错误:找不到符号

cannot find symbol
symbol:   variable s
location: class Instanceof

但是,以下(在我看来)等效的变体有效: 使用明确的 else 块:

static void doesWork(Object o) {
    if (o == null) {
        throw new Error();
    } else if (!(o instanceof String s)) {
        throw new Error();
    } else {
        System.out.println(s);
    }
}

或者没有 else:

static void doesWork(Object o) {
    if (o == null) {
        throw new Error();
    }
    if (!(o instanceof String s)) {
        throw new Error();
    }
    System.out.println(s);
}

或者用一个 if:

static void doesWork(Object o) {
    if (o == null || !(o instanceof String s)) {
        throw new Error();
    }
    System.out.println(s);
}

这是 javac 中的错误吗? 如果是,我应该报告这个,但具体在哪里?

【问题讨论】:

  • 相关部分是§6.3.1 - 错误表明javac 认为变量s 不在此范围内,因为错误是“s?What s?”。这就像一个javac 错误,但我还没有找到javac 在这里打破规范的确切章节。
  • 对我未经训练的眼睛来说,这似乎是个问题。我看不出您所展示的示例会起作用的任何合乎逻辑的原因,但那个不起作用。你可以在这里报告错误:bugreport.java.com/bugreport
  • 我怀疑这是 if-then-else 与 if-then 的可达性不同,具体取决于子句的主体。
  • 与 Eclipse 一起工作。作为旁注,null 支票在这里已过时......

标签: java language-lawyer java-17


【解决方案1】:

doesNotWork 的情况等价于:

static void doesNotWork(Object o) {
    if (o == null) {
        throw new Error();
    } else {
        if (!(o instanceof String s)) {
            throw new Error();
        }
    }
    System.out.println(s); // error here
}

这使得String s 位于由大括号包围的块内变得更加明显,因此超出了范围,就像这也不起作用一样:

static void doesNotWork(Object o) {
    {
        if (!(o instanceof String s)) {
            throw new Error();
        }
    }
    System.out.println(s); // error here
}

如果它确实起作用,在else里面加上println,就相当于这样:

if (o == null) {
    throw new Error();
} else {
    if (!(o instanceof String s)) {
        throw new Error();
    } else {
        System.out.println(s);
    }
}

这表明 println 在范围内。

【讨论】:

  • 似乎是一个合理的猜测,但如果只是范围问题,那么我会惊讶于 Eclipse 的编译器不会以同样的方式工作。
  • 现在,问题是,等同于块是否允许将代码视为在块内,即使不是? javac 似乎不一致。例如。如果我将代码更改为 do if(!(o instanceof String s)) throw new Error(); while(false); System.out.println(s);,它会编译代码(就像 Eclipse 所做的那样),尽管它等同于两个编译器都拒绝的 do { if(!(o instanceof String s)) throw new Error(); } while(false); System.out.println(s);。
  • 我可以使用相同的“this is基本上等同于that”的策略来表示int a = 5; System.out.println(a);等同于{int a = 5;} {System.out.println(a);},因此不应该编译:大括号定义范围,因此在这里想象它们不是一种有效的方法。我认为这确实解释了会发生什么,但我想说这意味着 JLS 被窃听或 javac 是。
  • @rzwitserloot 请注意,我有意搜索具有控制流构造的示例,而不是在任意语句周围放置{ },以表明即使那样,行为也不一致。如果 do … while 循环的内部语句仅在写为块时才被视为块,事实上,当没有块存在时,整个循环被视为单个语句,这应该适用于 else 子句也是。就像 Eclipse 一样,始终如一。
【解决方案2】:

已在 OpenJdk Jira 中创建了相关的 bug 票证。它被标记为可复制,因此很可能会被视为错误并已修复。

这里是aforementioned ticket,所以我们可以追踪进度。

编辑:根据上述票证,预计此错误问题将首先在 JDK 20 上修复。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-26
    相关资源
    最近更新 更多