Attraper une exception et la renvoyer, mais ce n'est pas une exception

10

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 Exceptionpuis le rejette, mais la méthode n'est pas déclarée pour lancer Exceptionet 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 Exceptiondans la run()méthode.

Informations supplémentaires

De la même manière, si doSomethingest déclaré pour lancer, IOExceptionil suffit IOExceptionde le déclarer dans la run()méthode -même s'il Exceptionest 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 Exceptiondans les extraits de code ci-dessus? (Si je voudrais l'ajouter, IntelliJ m'avertit qu'il Exceptionn'est jamais jeté).

Simon Forsberg
la source
3
Intéressant. Quel compilateur utilisez-vous? Si c'est un compilateur IDE, vérifiez avec javac- j'ai rencontré des cas où le compilateur Eclipse était plus indulgent.
M. Prokhorov
2
Je peux reproduire ce comportement sur openjdk-8. La compilation avec l' -source 1.6indicateur 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 compilation
Vogel612
1
semble que le compilateur est plus intelligent depuis Java 7 et effectue plus de vérifications sur les exceptions réelles qui pourraient être levées.
michalk
2
Cette question n'est pas un doublon et la réponse se trouve dans le lien que j'ai fourniIn 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
Le doublon actuellement marqué est certainement pertinent, mais ne fournit pas une réponse suffisamment détaillée IMO. Il y a un lien vers le JLS dans les commentaires de la réponse , à part cela, aucune information.
Simon Forsberg

Réponses:

0

Je n'ai pas parcouru le JLScomme 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 javacest assez "intelligent" dans certains cas (comme dans votre cas), mais laisse beaucoup d'autres choses à gérer plus tard JIT. Dans ce cas, c'est juste que le compilateur "peut dire" que seul un RuntimeExceptionserait intercepté. C'est évident, c'est la seule chose que vous lancez doSomething. Si vous modifiez légèrement votre code en:

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

vous verrez un comportement différent, car maintenant vous javacpouvez dire qu'il y a un nouveau Exceptionque 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:

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

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

Eugène
la source
J'ai lu dans un lien que quelqu'un a fourni que si vous réaffectez l'exception interceptée dans le bloc catch, le compilateur ne peut pas être intelligent. Je suppose que quelque chose de similaire s'applique dans ce cas. Le compilateur sait que l' ex2exception sera levée, elle a été initialement créée en tant que Exceptionmais est ensuite réaffectée à ex, et par conséquent le compilateur ne peut pas être intelligent.
Simon Forsberg
@SimonForsberg quelqu'un qui a une passion pour JLSpourrait venir et a fourni les citations nécessaires pour le prouver; malheureusement je ne les ai pas.
Eugene
Pour mémoire, lorsque je modifie le bloc catch pour contenir une réaffectation de l'exception interceptée à lui-même ( 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
Vogel612
Jetez un oeil à cette question qui est également un double. Celui-ci et le dup du possible dup l'expliquent et aussi des liens vers JLS.
michalk