在浏览SonarQube插件的一些代码时,我遇到了org.sonar.plugins.java.api.tree.TryStatementTree界面。实施的规则之一遍历了try-with-resources块中定义的变量。

查看TryStatementTree#resourceList,我可以看到它返回了ListTree<Tree>。这可以通过多种方式进行迭代。在一种情况下,插件会检查声明的变量的名称。发生这种情况时,将Tree强制转换为VariableTree,因为Tree是更通用的接口,无法通过IdentifierTree访问变量名。

这就是我正在查看的代码中的强制转换的地方,它是扩展BaseTreeVisitor并实现JavaFileScanner的类。

@Override
public void visitTryStatement(TryStatementTree tree) {
  // ...
  tree.resourceList().stream()
     .filter(resource -> resource instanceof VariableTree) // Is it possible for this condition to evaluate to false?
     .map(VariableTree.class::cast)
     .map(resource -> resource.simpleName().name())
     .forEach(SomeClass::handleThisCase);
  // ...
}


看着Java Language Standard,我想不出try语句代替资源声明而不是标识符列表的情况。

我认为这与表示不存在的资源定义或类似内容的需求有关,因此我在上面提出了一些单元测试用例。

try { // No resource declaration here
  // ...
} catch (SomeException ex) {
  // ...
}


但是在这种情况下,根本不会调用该方法,我猜这是由BaseTreeVisitor处理的。

我一直在尝试提出一些示例,这些示例将使强制转换变得不可能,但是我提出的所有内容要么无法编译,要么永远不会遵循该执行路径。

我是否缺少编写try语句的方法,该语句使更通用的Tree更好的选择?还是这种选择源于库中接口的结构方式?似乎没有任何超级接口(TryStatementTree-> StatementTree-> Tree)强制执行。 resourceListTryStatementTree本身定义。

最佳答案

具有讽刺意味的是,我在发布问题后的SonarQube documentation分钟内偶然发现了答案。

事实证明,有一个方法完全符合我的预期,TryStatementTree#resource,已被弃用,然后取而代之的是TryStatementTree#resourceList,这正是我的代码所使用的方法。


不赞成使用的方法org.sonar.plugins.java.api.tree.TryStatementTree.resources()已被删除,而支持org.sonar.plugins.java.api.tree.TryStatementTree.resourceList(),因为Java 9允许将VariableTree以外的其他树作为资源放置在try-with-resources语句中。


这是一个例子:

SomeAutoCloseableClass myResource = obtainOneSomehow();
try (myResurce) { // No resource declaration here, just an identifier
  // ...
} catch (SomeException ex) {
  // ...
}


我的项目是在源级别设置为Java 8兼容性的情况下编译的,应该对此进行修改,并应添加新的单元测试用例,以确保处理了写入try-with-resources块的新方法。

09-27 17:56