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 Exception
e o repete novamente, mas o método não está declarado para lançar Exception
e aparentemente não precisa ser. Esse código compila perfeitamente (pelo menos no Java 11).
Minha expectativa seria que eu tivesse que declarar throws Exception
no run()
método
Informação extra
De maneira semelhante, se doSomething
for declarado como lançado, IOException
ele só IOException
precisará ser declarado no run()
método-, mesmo que Exception
seja 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 Exception
nos trechos de código acima? (Se eu adicionar, o IntelliJ me avisa que Exception
nunca é lançado).
javac
- eu estive executando em casos em que o compilador Eclipse era mais branda.-source 1.6
sinalizador gera um erro de compilação conforme o esperado. Compilando com compatibilidade de origem 7 que não elevar o erro de compilaçãoIn 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.
Respostas:
Como não fiz a varredura
JLS
como 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 tardeJIT
. Nesse caso, é apenas que o compilador "pode dizer" que apenas umRuntimeException
seria capturado. Isso é óbvio, é a única coisa que você jogadoSomething
. Se você alterar ligeiramente o seu código para:você verá um comportamento diferente, porque agora
javac
pode dizer que há um novoException
que 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:
OMI, por causa
ex2 = ex;
disso, não deve falhar novamente, mas falha.Caso isso tenha sido compilado com
javac 13+33
fonte
ex2
exceção será lançada, ela foi originalmente criada como um,Exception
mas depois é reatribuída eex
, portanto, o compilador não pode ser inteligente.JLS
pode vir e fornecer as citações necessárias para provar isso; infelizmente eu não os tenho.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