【问题标题】:Dead code warning in try-with-resources, but not in translated try-catch-finallytry-with-resources 中的死代码警告,但翻译后的 try-catch-finally 中没有
【发布时间】:2016-09-20 15:55:24
【问题描述】:

以下代码使用 Java 8 中引入的 try-with-resources 构造。occasionallyThrow() 方法被声明为抛出 OccasionalException,即 Resourceclose() 方法抛出 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 中仍然存在死代码警告:

【问题讨论】:

  • 我不确定您是否可以捕获关闭期间抛出的异常,该异常是由 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


【解决方案1】:

由于某种原因,静态代码分析器认为资源将在 try 声明后立即关闭,而根据 this 教程,资源在语句后关闭。

try-with-resources 语句确保每个资源在语句结束时关闭。

因此,例如,如果您将代码更改为在值为 is(下面的代码)之后使用资源,它不会警告您有关死代码(但是在 Eclipse Luna 上测试)。

Object tryWithResources() throws OccasionalException {
    Object value = null;
    try (Resource resource = new Resource()) {
        occasionallyThrow();
        value = new Object();
        resource.someMethod(); // using the resource, so eclipse thinks it's not closed yet (correctly)
    }
    catch (CloseException e) {
        if (value == null) {
            // alive code
        }
        else {
            // dead code
        }
    }
    return value;
}

更新

这是我在设置值后使用资源(在本例中为阅读器)测试的实际代码。

        Object val = null;

        try (BufferedReader reader = new BufferedReader(new FileReader("C:\\file.txt"))) {
            val = new Object();
            System.out.println("got here");
            reader.readLine();
        }
        catch(IOException e){
            System.out.println("io ex");
            if ( val == null){

            }
            else{

            }
        }

【讨论】:

  • 在设置 value 后使用资源并不能消除 Eclipse 中的死代码警告。我已更新问题以显示警告仍然存在。
  • 那么它可能是 Eclipse Neon 特有的。我在 Eclipse Luna 中尝试过。
  • 不,我在 Luna 和 Eclipse 中都试过了。警告存在于它们两者中。我再次更新了我的问题以显示两者。
  • 有趣。我也试过霓虹灯。它在霓虹灯中也消失了。我更新了答案以包含实际代码(我正在尝试使用 BufferedReader,因为我没有您的代码)
  • 好的,我想我知道那是什么。 readLine() 方法也会抛出 IOException,这可能就是为什么 eclipse 现在确信可以在设置值后抛出异常。
【解决方案2】:

这并不能回答 为什么 Eclipse 会生成警告的问题,或者是否应该生成警告,但这是一种解决方法,至少可以暂时消除警告存在。与其将条件放在 catch 块中,不如调用另一个方法,其中包含正在测试的值以及异常,并从该方法中测试对象是否为空,然后执行任何需要的操作完成:

Object tryWithResources() throws OccasionalException {
    Object value = null;
    try (Resource resource = new Resource()) {
        occasionallyThrow();
        value = new Object();
        System.out.println(resource.toString());
    }
    catch (CloseException e) {
        catchBlock(value, e);  // call auxiliary method
    }
    return value;
}

void catchBlock(Object value, CloseException e) {
    if (value == null) {
        // then
    }
    else {
        // else
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-27
    • 2014-12-18
    • 1970-01-01
    • 2011-06-01
    • 1970-01-01
    • 2014-12-09
    • 1970-01-01
    • 2023-03-09
    相关资源
    最近更新 更多