Подтвердить что ты не робот

Что же такое "Самоудерживание не разрешено" и почему код Javac генерирует код, который приводит к этой ошибке?

Эта новая конструкция 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();
}

Запустив этот пример, я получаю:

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

Читая документацию, они, похоже, говорят, что если бы вы добавили исключенное исключение к себе, это и вызывает эту ошибку. Но я этого не делаю, я просто использую блок try-with-resources. Затем компилятор Java генерирует то, что кажется незаконным кодом, что делает эту функцию непригодной для использования.

Конечно, когда тест проходит, проблем не возникает. И когда тест не выполняется, возникает исключение. Итак, теперь, когда я исправил проблему, которую я изначально обнаружил, я вернулся к использованию try-with-resources. Но в следующий раз, когда произойдет исключение, я предпочел бы скорее исключение ожидания, вместо того, чтобы одна Java сама испустила, по-видимому, не очень вескую причину.

Итак... есть ли способ получить правильное сообщение об ошибках здесь, не отказываясь от try-with-resources?

4b9b3361

Ответ 1

Похоже, что 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();
}

Если это так, я думаю, что это проблема jMock, а не компилятора Java.

Ответ 2

У меня была проблема в Apache Commons VFS (Unit Test не удалось на Java 8, см. VFS-521). И получается, что java.io.FilterOutputStream использует функцию try-with-resource (подавленное исключение) таким образом, с которой она не может справиться flush + close, бросая одно и то же исключение.

И что еще хуже, перед Java 8 он просто молчаливо проглатывает исключения из вызова flush(), см. JDK-6335274).

Я исправил его, вообще избегая super.close(). В настоящее время обсуждается это на сервере corelibs-dev openjdk mailingl ist: http://openjdk.5641.n7.nabble.com/FilterOutputStream-close-throws-exception-from-flush-td187617.html