【发布时间】:2016-09-20 15:55:24
【问题描述】:
以下代码使用 Java 8 中引入的 try-with-resources 构造。occasionallyThrow() 方法被声明为抛出 OccasionalException,即 Resource 的 close() 方法抛出 CloseException。 Eclipse(版本:Neon Release (4.6.0),Build id:20160613-1800)在标有 // dead code 的行上添加了一个警告,即分支是死代码。 Eclipse 隐含地确认标有 // alive code 的行是 not dead code。
Object tryWithResources() throws OccasionalException {
Object value = null;
try (Resource resource = new Resource()) {
occasionallyThrow();
value = new Object();
}
catch (CloseException e) {
if (value == null) {
// alive code
}
else {
// dead code
}
}
return value;
}
我对此感到困惑。如果 occasionalallyThrow() 抛出其 OccasionalException,则 try-with-resources 应将其作为主要异常捕获,然后尝试关闭资源。如果关闭资源抛出CloseException,那么它会在OccasionalException下被抑制,所以不会有CloseException被捕获。因此,CloseException 应该捕获的唯一时间是当 try 中的块成功完成时,这意味着 value 是非空的。所以看起来“死代码”实际上是活的,而“活代码”实际上是死的。我不确定编译器实际上应该在这里识别什么,但至少,这里的“死代码”似乎应该不被称为死代码。
使这更复杂的是,没有使用 try-with-resources 表单的翻译表单根本不会被标记任何死代码警告。 (基于14.20.3.2. Extended try-with-resources,我相当有信心我得到了正确的翻译,但如果这里有错误,我不会完全感到惊讶……)
Object expandedTry() throws OccasionalException {
Object value = null;
try {
Resource resource = new Resource();
Throwable $primary = null;
try {
occasionallyThrow();
value = new Object();
}
catch (Throwable t) {
$primary = t;
throw t;
}
finally {
if (resource != null) {
if ($primary != null) {
try {
resource.close();
}
catch (Throwable $suppressed) {
$primary.addSuppressed($suppressed);
}
}
else {
resource.close();
}
}
}
}
catch (CloseException e) {
if (value == null) {
// alive (not dead!)
}
else {
// alive
}
}
return value;
}
我是否遗漏了一些东西,会使 if-else 中的任何一个分支在其中一个中死亡,但在另一个中没有?
完整代码
这是包含辅助异常类型、Resource 类和顶级类定义的完整代码。
public class TestTryWithResources {
/** Exception thrown by Resource's close() method */
@SuppressWarnings("serial")
static class CloseException extends Exception {}
/** AutoCloseable declared to throw a CloseException */
static class Resource implements AutoCloseable {
@Override
public void close() throws CloseException {}
}
/** An occasionally thrown exception */
@SuppressWarnings("serial")
static class OccasionalException extends Exception {}
/** Method declared to throw an occasional exception */
void occasionallyThrow() throws OccasionalException {}
/*
* Method using try-with-resources. Eclipse warns that the
* portion marked with "// dead code" is Dead code.
*/
Object tryWithResources() throws OccasionalException {
Object value = null;
try (Resource resource = new Resource()) {
occasionallyThrow();
value = new Object();
}
catch (CloseException e) {
if (value == null) {
// alive code
}
else {
// dead code
}
}
return value;
}
/*
* Method not using try-with-resources. This is the translation
* of the try-with-resources in tryWithResources, according to
* [14.20.3 try-with-resources][1]. Eclipse does not warn about
* any of the code being Dead code.
*
* [1]: https://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.3
*/
Object expandedTry() throws OccasionalException {
Object value = null;
try {
Resource resource = new Resource();
Throwable $primary = null;
try {
occasionallyThrow();
value = new Object();
}
catch (Throwable t) {
$primary = t;
throw t;
}
finally {
if (resource != null) {
if ($primary != null) {
try {
resource.close();
}
catch (Throwable $suppressed) {
$primary.addSuppressed($suppressed);
}
}
else {
resource.close();
}
}
}
}
catch (CloseException e) {
if (value == null) {
// alive
}
else {
// alive
}
}
return value;
}
}
评论回复
Amin J's answer 建议在设置 value 后使用资源来更改 Eclipse 的代码分析。但这不起作用。使用资源后,例如通过打印,Luna 和 Neon 中仍然存在死代码警告:
【问题讨论】:
-
也许这个Bug 366277 - [1.7] Incorrect dead code/null value analysis in try-with-resources,虽然现在已经5岁了......
-
我不确定您是否可以捕获关闭期间抛出的异常,该异常是由 try-with-resources 在属于 try-block 的 catch-block 中完成的。如果将 CloseException 更改为受检异常会发生什么?如果你另外将 catch 移动到周围的 try 会发生什么?
-
@mm759 先回答中间问题:CloseException is 已检查异常。我通过在此处将其定义为 Exception 的子类(而不是 RuntimeException 的子类)来确保这一点。
-
@mm759 对于第一个问题,14.20.3.2. Extended try-with-resources 描述了带有 catch 的 try-with-resources 的行为。
try ResourceSpecification Block Catches Finally(这是我正在使用的(没有可选的finally),被翻译为try { try ResourceSpecification Block } Catches Finally。所以你可以在body没有抛出时捕获隐式关闭抛出的异常一个例外,所以(第二个问题)翻译已经“将 [s] 捕获到周围的尝试 [.]” -
它可能不是“已确认”,但我个人可以确认它仍然发生在 Eclipse 4.5.2 上。具体来说,标记为“死代码”的代码在运行测试用例时会清楚地生成输出。再一次,只有 Eclipse 生成警告 - 不是 javac
标签: java eclipse try-with-resources