Je suis tombé sur un code ressemblant à ceci:
void run() {
try {
doSomething();
} catch (Exception ex) {
System.out.println("Error: " + ex);
throw ex;
}
}
void doSomething() {
throw new RuntimeException();
}
Ce code me surprend car il semble que la run()
méthode-soit capable de lancer un Exception
, car il l'attrape Exception
puis le rejette, mais la méthode n'est pas déclarée pour lancer Exception
et n'a apparemment pas besoin de l'être. Ce code se compile très bien (en Java 11 au moins).
Je m'attendrais à devoir déclarer throws Exception
dans la run()
méthode.
Informations supplémentaires
De la même manière, si doSomething
est déclaré pour lancer, IOException
il suffit IOException
de le déclarer dans la run()
méthode -même s'il Exception
est capturé et renvoyé.
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 ...
}
Question
Java aime généralement la clarté, quelle est la raison de ce comportement? A-t-il toujours été comme ça? Qu'est-ce qui, dans la spécification du langage Java, permet à la run()
méthode de ne pas avoir à déclarer throws Exception
dans les extraits de code ci-dessus? (Si je voudrais l'ajouter, IntelliJ m'avertit qu'il Exception
n'est jamais jeté).
javac
- j'ai rencontré des cas où le compilateur Eclipse était plus indulgent.-source 1.6
indicateur soulève une erreur de compilation comme prévu. La compilation avec la compatibilité avec la source 7 ne soulève pas l'erreur de compilationIn 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.
Réponses:
Je n'ai pas parcouru le
JLS
comme vous l'avez demandé dans votre question, alors veuillez prendre cette réponse avec un grain de sel. Je voulais en faire un commentaire, mais il aurait été trop gros.Je trouve parfois drôle, comment
javac
est assez "intelligent" dans certains cas (comme dans votre cas), mais laisse beaucoup d'autres choses à gérer plus tardJIT
. Dans ce cas, c'est juste que le compilateur "peut dire" que seul unRuntimeException
serait intercepté. C'est évident, c'est la seule chose que vous lancezdoSomething
. Si vous modifiez légèrement votre code en:vous verrez un comportement différent, car maintenant vous
javac
pouvez dire qu'il y a un nouveauException
que vous lancez, sans rapport avec celui que vous avez attrapé.Mais les choses sont loin d'être idéales, vous pouvez encore "tromper" le compilateur via:
OMI, à cause de
ex2 = ex;
cela ne devrait pas échouer à nouveau, mais c'est le cas.Juste au cas où cela a été compilé avec
javac 13+33
la source
ex2
exception sera levée, elle a été initialement créée en tant queException
mais est ensuite réaffectée àex
, et par conséquent le compilateur ne peut pas être intelligent.JLS
pourrait venir et a fourni les citations nécessaires pour le prouver; malheureusement je ne les ai pas.ex = ex;
), l'heuristique n'est plus appliquée. Ce comportement semble être appliqué à tous les niveaux de source de 7 à 11 et probablement 13