57 votes

Code inaccessible compilant sans erreur - Comment ?

À ma connaissance, le code suivant ne devrait pas compiler car l'instruction "I am unreachable" se trouve après le return.

Cependant, lorsque j'ai exécuté ce code, il compile parfaitement.

Aussi, d'après la JLS : Instructions inaccessibles, cela ne devrait pas compiler.

d'après la spécification, à 14.21 Instructions inaccessibles :

Une instruction try peut se terminer normalement si les deux conditions suivantes sont vraies :

  • Le bloc try peut se terminer normalement ou n'importe quel bloc catch peut se terminer normalement.

  • Si l'instruction try a un bloc finally, alors le bloc finally peut se terminer normalement.

Ici, le bloc try ne peut pas se terminer normalement mais le bloc catch ainsi que le bloc finally peuvent le faire, donc je suis confus ici

    public class Test1 {
     public static void main(String[] args) {
        try {
            return;

        } catch (Exception e) {
            System.out.println("catch");

        } finally {
            System.out.println("finally");
        }
        System.out.println("I am unreachable??!!!");
    }
}

Quelqu'un peut-il m'aider à comprendre ce comportement ?

23 votes

Eh bien, en cas d'exception, le code pourrait être atteignable. Peut-être que le compilateur l'a remarqué.

1 votes

Avez-vous envisagé le cas de capture ? Dans le bloc try, que retournerez-vous s'il y a une exception levée ?

3 votes

@TimBiegeleisen A quel type d'exception tu penses qu'elle sera levée dans cet exemple?

72voto

Eran Points 35360

Je pense que ce sont les citations pertinentes de JLS 14.21 :

  • Un bloc vide qui n'est pas un bloc switch peut se terminer normalement s'il est accessible.

    Un bloc non vide qui n'est pas un bloc switch peut se terminer normalement si la dernière instruction à l'intérieur peut se terminer normalement.

    La première instruction dans un bloc non vide qui n'est pas un bloc switch est accessible si le bloc est accessible.

    Chaque autre instruction S dans un bloc non vide qui n'est pas un bloc switch est accessible si l'instruction précédant S peut se terminer normalement.

Donc votre

System.out.println("Suis-je inaccessible ??!!!");

est accessible si l'instruction try peut se terminer normalement, ce qui nous mène à la citation suivante :

  • Une instruction try peut se terminer normalement si les deux conditions suivantes sont vraies :

    • Le bloc try peut se terminer normalement ou un bloc catch quelconque peut se terminer normalement.

    • Si l'instruction try a un bloc finally, alors le bloc finally peut se terminer normalement.

Étant donné que votre bloc catch peut se terminer normalement et vous avez un bloc finally qui peut se terminer normalement, l'instruction try peut se terminer normalement. Par conséquent, l'instruction System.out.println("Suis-je inaccessible ??!!!"); qui suit est considérée comme accessible, peu importe l'instruction return; à l'intérieur du bloc try.

Remarquez le ou dans

Le bloc try peut se terminer normalement ou un bloc catch quelconque peut se terminer normalement.

Cela signifie que soit le bloc try soit au moins l'un des blocs catch doivent se terminer normalement. Ils ne doivent pas nécessairement tous les deux se terminer normalement.

Enfin, la logique derrière ce comportement :

Le compilateur n'est pas censé analyser si un bloc try peut ou non lever une Exception. La raison en est que la hiérarchie de classes Exception inclut à la fois des exceptions vérifiées et des exceptions non vérifiées, et les exceptions non vérifiées ne sont pas déclarées dans des clauses throws (si vous remplacez Exception par une exception vérifiée, comme IOException, le compilateur vous signalerait que votre bloc try ne lance jamais cette exception, rendant le bloc catch inaccessible).

Par conséquent, étant donné que vous avez un bloc catch (Exception e) qui peut se terminer normalement, le compilateur suppose que ce bloc catch est accessible, et donc que l'ensemble de l'instruction try peut se terminer normalement, même si le bloc try ne peut pas se terminer normalement.

Le bloc finally, s'il est présent, doit également pouvoir se terminer normalement, car le bloc finally est également exécuté, donc s'il ne pouvait pas se terminer normalement, l'ensemble de l'instruction try ne pourrait pas se terminer normalement.

7 votes

Une chose que j'ajouterais - InterruptedException.

13 votes

@chrylis que rajouteriez-vous à propos de InterruptedException?

45 votes

Je pense que chrylis y arrivait, mais il a été interrompu.

15voto

Aman Chhabra Points 328

Vous avez un return dans le bloc try.

Que se passe-t-il s'il y a une exception et qu'elle va directement au bloc catch. Il n'est donc pas inaccessible en termes de compilateur et se compile avec succès.

La compilation échouera si vous avez également un return dans le bloc catch

Aussi, selon JLS 14.21:

Une instruction break atteignable sort d'une instruction si, à l'intérieur de la cible du break, il n'y a pas de blocs try dont les blocs try contiennent l'instruction break, ou s'il y a des blocs try dont les blocs try contiennent l'instruction break et que toutes les clauses finally de ces blocs try peuvent se terminer normalement.

Voici la sortie ci-dessous lorsque vous avez un return à la fois dans le bloc try et catch :

jshell>  public class Test1 {
   ...>     public static void main(String[] args) {
   ...>         try {
   ...>             return;
   ...>
   ...>         } catch (Exception e) {
   ...>             return;
   ...>
   ...>         }
   ...>
   ...>         System.out.println("Je suis inatteignable ??!!!");
   ...>     }
   ...> }
|  Erreur:
|  instruction inatteignable
|          System.out.println("Je suis inatteignable ??!!!");
|          ^------------------------------------------^

De même, si vous avez un return dans votre instruction finally, la compilation échouera.

Une instruction après un bloc try sera considérée comme atteignable si :

1) Le bloc Try a une instruction return avec un bloc catch et finalement sans instruction return
2) Le bloc Try n'a pas d'instruction return avec un bloc catch ayant ou n'ayant pas d'instruction return et finalement sans instruction return
3) Le bloc Try, catch et finalement n'ont pas d'instruction return

0 votes

J'ai édité ma question maintenant enfin est également là, donc selon votre analyse enfin sera exécuté

0 votes

Oui, dans ce cas, enfin sera exécuté et devrait fonctionner sans aucune compilation

10voto

Yashi Srivastava Points 192

En essayant de donner une raison plus simplifiée pour le problème, le code est atteignable, dans le cas où une exception survient dans le bloc essayer. Dans ce cas, le contrôle passe ensuite au bloc catch puis au bloc enfin. Après le bloc enfin, l'instruction particulière sera exécutée.

try {
            return;                                 //line 1

        } catch (Exception e) {
            System.out.println("catch");            //line 2

        } finally {
            System.out.println("finally");          //line 3
        }
        System.out.println("Je suis inatteignable ??!!"); //line 4

Cela signifie qu'il y a 2 cas, donc 2 flux:

  1. ligne 1 -> ligne 3 -> return (dans le cas où il n'y a pas d'exception)
  2. ligne 1 (une exception se produit) -> ligne 2 -> ligne 3 -> ligne 4 (dans le cas où try reçoit une exception)

La ligne deviendra inatteignable, seulement si nous ne laissons aucune possibilité pour que le contrôle y aille. Il y a 2 façons pour cela :

  1. retourner du bloc catch
  2. retourner du bloc enfin.

Dans les deux cas, le contrôle ne pourra jamais s'écouler jusqu'à cette ligne.

try {
            return;                                 //line 1

        } catch (Exception e) {
            System.out.println("catch");            //line 2
            return;                                 //retour de contrôle
        } finally {
            System.out.println("finally");          //line 3
            return;                                 //ou retourner d'ici
        }
        System.out.println("Je suis inatteignable ??!!"); //line 4

J'espère que ça donne maintenant une image claire de la vraie raison du problème.

0 votes

Bien qu'il ait de la valeur dans le cas présent en montrant des façons de rendre la dernière ligne inaccessible, à ma connaissance, une déclaration return dans un bloc finally est un code smell. Il vaut mieux l'éviter.

0 votes

@KevinLH pouvez-vous s'il vous plaît me dire un scénario dans lequel un retour dans finally est un code de mauvaise qualité, car je n'en trouve aucun. Cela est seulement nécessaire à titre d'information.

0 votes

@KevinLH Je suis d'accord avec ton point, mais je suis aussi en faveur de yashi car le code a été écrit pour démontrer le cas d'utilisation et donner une meilleure compréhension de l'utilisation du code inaccessible.

4voto

gnasher729 Points 5011

Lorsque vous regardez les "déclarations inatteignables" dans un programme Java, ce qui compte est ce que la définition du langage dit, pas ce qu'un compilateur astucieux pourrait trouver.

Selon le langage Java, le dernier println n'est pas une déclaration inatteignable. Même si en regardant le code il est facile (pour un humain astucieux) de comprendre qu'il ne pourra jamais s'exécuter.

Un langage de programmation doit s'appuyer sur des règles fixes faciles à suivre exactement pour le compilateur. Un compilateur ne peut pas compter sur l'astuce, car les différents compilateurs auraient des niveaux différents d'astuce, donc s'ils ne suivaient pas les règles simples et fixes, certains compilateurs trouveraient la déclaration inatteignable, et d'autres non.

0 votes

Ce serait une réponse plus claire si vous expliquiez POURQUOI la dernière ligne est accessible (votre deuxième paragraphe, mais pas les deux autres). Mais en tout cas la réponse est incorrecte! Parce que la dernière ligne de code EST accessible (si une exception est jamais attrapée).

0 votes

@andrewf Eh bien, l'exemple dans la question (un bloc ne contenant rien d'autre qu'une seule instruction return;) ne peut pas déclencher d'erreur à ma connaissance (dans un environnement d'exécution moderne qui n'implémente pas Thread.stop - voir également le commentaire de Holger)

0 votes

@Hulk, bon point! (Je suppose que je lisais cette ligne comme du code suppléant pour quelque chose qui FESAIT quelque chose qui pourrait lancer une erreur, mais bien sûr tu as raison.)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X