究竟是什么"不允许自我抑制",为什么Javac会产生导致此错误的代码?

Tre*_*kaz 14 java exception java-7 try-with-resources

这个新的Java 7 try-with-resources构造非常好.或者至少,这很好的,直到一个例外走过来,毁了我的一天.

我终于把它归结为一个可重复的测试,它只使用JUnit + jMock.

@Test
public void testAddSuppressedIssue() throws Exception {
    Mockery mockery = new Mockery();
    final Dependency dependency = mockery.mock(Dependency.class);

    mockery.checking(new Expectations() {{
        allowing(dependency).expectedCall();
        allowing(dependency).close();
    }});

    try (DependencyUser user = new DependencyUser(dependency)) {
        user.doStuff();
    }
}

// A class we're testing.
private static class DependencyUser implements Closeable {
    private final Dependency dependency;

    private DependencyUser(Dependency dependency) {
        this.dependency = dependency;
    }

    public void doStuff() {
        dependency.unexpectedCall(); // bug
    }

    @Override
    public void close() throws IOException {
        dependency.close();
    }
}

// Interface for its dependent component.
private static interface Dependency extends Closeable {
    void expectedCall();
    void unexpectedCall();
}
Run Code Online (Sandbox Code Playgroud)

运行这个例子,我得到:

java.lang.IllegalArgumentException: Self-suppression not permitted
    at java.lang.Throwable.addSuppressed(Throwable.java:1042)
    at com.acme.Java7FeaturesTest.testTryWithResources(Java7FeaturesTest.java:35)
Run Code Online (Sandbox Code Playgroud)

阅读文档时,他们似乎在说如果你要将一个被抑制的异常添加回自身,那就是触发这个错误的原因.但不这样做,只是使用try-with-resources块.然后Java编译器生成看似非法的代码,这使得该功能实际上无法使用.

当然,当测试通过时,不会出现问题.当测试失败时,会发生异常.所以现在我已经修复了我最初发现的问题,我已经恢复使用try-with-resources.但是下次发生异常时,我宁愿异常是期望失败,而不是一个Java本身已经发出似乎没有充分理由.

那么......有没有办法在这里获得正确的错误报告,而不放弃尝试资源?

axt*_*avt 7

看起来jMock会从两个方法中抛出相同的异常实例.这就是如何在没有jMock的情况下再现它:

public class Test implements Closeable {
    private RuntimeException ex = new RuntimeException();

    public void doStuff() {
        throw ex;
    }

    public void close() {
        throw ex;
    }
}

try (Test t = new Test()) {
    t.doStuff();
}
Run Code Online (Sandbox Code Playgroud)

如果是这样,我认为这是jMock而不是Java编译器的问题.

  • @Trejkaz一般情况下重用实例并没有错,你只是不能将自引用传递给[`addSuppressed()`](http://docs.oracle.com/javase/7/docs/api/java /lang/Throwable.html#addSuppressed(java.lang.Throwable)).因为`close()`中的异常没有*导致try-with-resources块中的异常,所以`close()`异常被添加到来自`unexpectedCall()`的异常的抑制列表中.由于此处的重用,这会导致异常尝试抑制自身,这是方法描述所不允许的. (2认同)