Capturando uma exceção e repetindo-a, mas não é uma exceção

10

Eu me deparei com código parecido com isto:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        System.out.println("Error: " + ex);
        throw ex;
    }
}

void doSomething() {
    throw new RuntimeException();
}

Esse código me surpreende porque parece que o run()-method é capaz de lançar um Exception, já que ele captura Exceptione o repete novamente, mas o método não está declarado para lançar Exceptione aparentemente não precisa ser. Esse código compila perfeitamente (pelo menos no Java 11).

Minha expectativa seria que eu tivesse que declarar throws Exceptionno run()método

Informação extra

De maneira semelhante, se doSomethingfor declarado como lançado, IOExceptionele só IOExceptionprecisará ser declarado no run()método-, mesmo que Exceptionseja capturado e reapresentado.

void run() throws IOException {
    try {
        doSomething();
    } catch (Exception ex) {
        System.out.println("Error: " + ex);
        throw ex;
    }
}

void doSomething() throws IOException {
    // ... whatever code you may want ...
}

Questão

Java geralmente gosta de clareza, qual é a razão por trás desse comportamento? Sempre foi assim? O que na especificação da linguagem Java permite que o run()método não precise declarar throws Exceptionnos trechos de código acima? (Se eu adicionar, o IntelliJ me avisa que Exceptionnunca é lançado).

Simon Forsberg
fonte
3
Interessante. Qual compilador você está usando? Se for um compilador IDE, verifique com javac- eu estive executando em casos em que o compilador Eclipse era mais branda.
Prokhorov
2
Eu posso reproduzir esse comportamento no openjdk-8. Notavelmente, a compilação com o -source 1.6sinalizador gera um erro de compilação conforme o esperado. Compilando com compatibilidade de origem 7 que não elevar o erro de compilação
Vogel612
11
parece que o compilador é mais inteligente desde o Java 7 e faz mais verificações na exceção real que poderia ser lançada.
Michalk
2
Esta questão não é uma duplicata e a resposta pode ser encontrada no link eu fornecidoIn detail, in Java SE 7 and later, when you declare one or more exception types in a catch clause, and rethrow the exception handled by this catch block, the compiler verifies that the type of the rethrown exception meets the following conditions : 1. 1. The try block is able to throw it. 2. There are no other preceding catch blocks that can handle it. 3. It is a subtype or supertype of one of the catch clause's exception parameters.
Michalk
2
A duplicata atualmente marcada é definitivamente relevante, mas não fornece uma resposta IMO suficientemente detalhada. Há um link para o JLS nos comentários da resposta , além de nenhuma informação.
Simon Forsberg

Respostas:

0

Como não fiz a varredura JLScomo você fez na sua pergunta, leve essa resposta com um grão de sal. Eu queria fazer um comentário, mas teria sido muito grande.


Às vezes, acho engraçado como javacé "inteligente" em alguns casos (como no seu caso), mas deixa muitas outras coisas para serem tratadas mais tarde JIT. Nesse caso, é apenas que o compilador "pode ​​dizer" que apenas um RuntimeExceptionseria capturado. Isso é óbvio, é a única coisa que você joga doSomething. Se você alterar ligeiramente o seu código para:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        Exception ex2 = new Exception();
        System.out.println("Error: " + ex);
        throw ex2;
    }
}

você verá um comportamento diferente, porque agora javacpode dizer que há um novo Exceptionque você está lançando, não relacionado ao que você pegou.

Mas as coisas estão longe do ideal, você pode "enganar" o compilador novamente através de:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        Exception ex2 = new Exception();
        ex2 = ex;
        System.out.println("Error: " + ex);
        throw ex2;
    }
}

OMI, por causa ex2 = ex;disso, não deve falhar novamente, mas falha.

Caso isso tenha sido compilado com javac 13+33

Eugene
fonte
Eu li em algum link que alguém forneceu que, se você reatribuir a exceção capturada no bloco de captura, o compilador não poderá ser inteligente. Presumo que algo semelhante se aplique neste caso. O compilador sabe que a ex2exceção será lançada, ela foi originalmente criada como um, Exceptionmas depois é reatribuída e ex, portanto, o compilador não pode ser inteligente.
Simon Forsberg
@SimonForsberg alguém apaixonado por ela JLSpode vir e fornecer as citações necessárias para provar isso; infelizmente eu não os tenho.
Eugene
Para o registro, quando altero o bloco catch para conter uma nova designação da exceção capturada para si mesmo ( ex = ex;), a heurística não é mais aplicada. Esse comportamento parece ser aplicado a todos os níveis de fonte de 7 a 11 e provavelmente 13
Vogel612 17/17/19
Dê uma olhada nesta pergunta, que também é uma duplicata. Este e o dup do possível dup o explicam e também vinculam ao JLS.
Michalk