【问题标题】:Uncommonly used Java syntax (JavaParser)?不常用的Java语法(JavaParser)?
【发布时间】:2014-07-17 07:55:23
【问题描述】:

我正在探索一个 Java 语法解析器,我遇到了一段我通常不会在普通代码中使用的奇怪代码。取自 https://code.google.com/p/javaparser/source/browse/branches/mavenized/JavaParser/src/main/java/japa/parser/ASTParser.java#1998

它有许多包含代码的功能,例如

final public NameExpr Name() throws ParseException {
    NameExpr ret;
    jj_consume_token(IDENTIFIER);
    ret = new NameExpr(token.beginLine, token.beginColumn, token.endLine, token.endColumn, token.image);
    label_23: while (true) {
        if (jj_2_17(2)) {
            ;
        } else {
            break label_23;
        }
        jj_consume_token(DOT);
        jj_consume_token(IDENTIFIER);
        ret = new QualifiedNameExpr(ret.getBeginLine(), ret.getBeginColumn(), token.endLine, token.endColumn, ret, token.image);
    }
    {
        if (true) {
            return ret;
        }
    }
    throw new Error("Missing return statement in function");
}

乍一看它看起来很奇怪,但毫无疑问它是有效的,因为我可以编译它。但是有人可以解释它是如何工作的吗?我试图输入无效的 Java 语法,它确实有效!我很困惑。几行返回后如何抛出异常?

【问题讨论】:

  • 确保识别出无法理解的确切代码/语法:即它是“标签”吗? “空话”? “无用的投掷”?
  • 我无法真正提供答案,因为我从未遇到过这样的事情,但这似乎在this Oracle 教程中有所概述。
  • 是的,我指的是块标签、围绕返回的额外大括号、无用的 if 语句和无法访问的 throw。
  • 它看起来像一个生成的代码。也许来自 ANTRL?
  • 打破标签是 goto 的邪恶表弟

标签: java algorithm syntax syntax-error


【解决方案1】:

这确实是有效的代码,没有看到所有内容,我可以看到一些奇怪的东西:

  • “不正确”的变量和方法命名,有时使用 PascalCase。
  • 实例变量token
  • 静态变量IDENTIFIER

然后:

label_23: while (true) {
    if (jj_2_17(2)) {
        ;
    } else {
        break label_23;
    }
    jj_consume_token(DOT);
    jj_consume_token(IDENTIFIER);
    ret = new QualifiedNameExpr(ret.getBeginLine(), ret.getBeginColumn(), token.endLine, token.endColumn, ret, token.image);
}

这是一个无限循环,只要jj_2_17(2) 返回true,它就会一直运行,但似乎对该结果没有任何作用。当表达式为false 时,它脱离 label_23。为了让未来的读者更加困惑,它实际上只有在表达式为true(因为它在false 上中断)时才执行,即最后三行。

更多信息,label_23 只是一个标签,只能用于whilefor 循环。然后,您可以在使用break labelName;跳出该循环。

从内循环中跳出外循环的示例:

outerLoop: for (int i = 0; i < max; i++) {
    innerLoop: for (int j = 0; j < max2 - i; j++) {
        if (something) {
            break outerLoop;
        }
        //...
    }
}

您实际上也可以将continue 与标签结合使用。

然后我们看到一个没有保护的作用域块总是返回ret

{
    if (true) {
        return ret;
    }
}

所以这一切都是有效的。我认为我们也很有可能得出结论,这段代码是机器生成的。

【讨论】:

  • 看起来这个类是手动完成的,考虑到它的大小并且必须手动编码所有标签?
  • @Nederalm 我会说这段代码有很强的迹象表明它是机器生成的。
  • 回答您关于标签只能与 while 或 for 循环一起使用的观点,技术上不仅如此。 break 语句对于没有循环的标记块也有效。查看我的编辑。
【解决方案2】:

这里可能主要混淆的是 if-else 块在 while 中。

if (jj_2_17(2)) {
    ;
} else {
    break label_23;
}
jj_consume_token(DOT);
jj_consume_token(IDENTIFIER);
ret = new QualifiedNameExpr(ret.getBeginLine(), ret.getBeginColumn(), token.endLine, token.endColumn, ret, token.image);

这实际上只是一个否定的 if:

if(!jj_2_17(2)) {
    break label_23;
}
jj_consume_token(DOT);
jj_consume_token(IDENTIFIER);
ret = new QualifiedNameExpr(ret.getBeginLine(), ret.getBeginColumn(), token.endLine, token.endColumn, ret, token.image);

所以实际上这段代码只是返回最后一个可以被解析的QualifiedNameExpr(/*...*/);

关于您在 cmets 中提到的“无法到达的投掷”。想象一下当您创建一个方法(在 eclipse 或 netbeans 中)并且不添加 return 语句时会发生什么。

生成的代码(假设这是这里生成的代码)然后不包含 return 语句,突然你会抛出错误。这会导致 eclipse/netbeans/[在此处插入 IDE] 告诉您“函数中缺少返回语句”,这正是该语句的用途......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-07-31
    • 1970-01-01
    • 2020-05-17
    • 1970-01-01
    • 2014-06-25
    • 2012-03-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多