Quelqu'un sait-il pourquoi:
public void foo()
{
System.out.println("Hello");
return;
System.out.println("World!");
}
Serait signalé comme une "erreur inaccessible" sous Eclipse, mais
public void foo()
{
System.out.println("Hello");
if(true) return;
System.out.println("World!");
}
Déclenche uniquement un avertissement "Code mort"?
La seule explication à laquelle je peux penser est que le compilateur Java ne signale que la première, et qu'une analyse supplémentaire dans Eclipse résume la seconde. Cependant, si tel est le cas, pourquoi ne peut-il pas le compilateur Java comprendre ce cas au moment de la compilation?
Le compilateur Java ne comprendrait-il pas au moment de la compilation que le if (true) n'a aucun effet, produisant ainsi un bytecode qui est essentiellement identique? À quel moment l'analyse du code accessible est-elle appliquée?
Je suppose qu'une façon plus générale de penser à cette question est: "quand l'analyse de code accessible est-elle appliquée"? Dans la transformation du deuxième Java en bytecode final, je suis sûr qu'à un moment donné, l'équivalent d'exécution "if (true)" est supprimé et que les représentations des deux programmes deviennent identique. Le compilateur Java ne réappliquerait-il pas alors son analyse de code accessible?
Le premier compile pas (vous avez une erreur), le second compile (vous venez de recevoir un avertissement). Voilà la différence.
Quant à savoir pourquoi Eclipse détecte le code mort, eh bien, c'est juste la commodité d'un outil de développement intégré avec un compilateur intégré qui peut être affiné davantage par rapport à JDK pour détecter ce type de code.
pdate: le JDK élimine en fait le code mort.
public class Test {
public void foo() {
System.out.println("foo");
if(true)return;
System.out.println("foo");
}
public void bar() {
System.out.println("bar");
if(false)return;
System.out.println("bar");
}
}
javap -c
dit:
public class Test étend Java.lang.Object { public Test (); Code: 0: aload_0 1: invokespecial # 1 ; // Méthode Java/lang/Object. "" :() V 4: retourne Public void foo (); Code: 0: getstatic # 2; // Champ Java/lang/System.out: Ljava/io/PrintStream; 3: ldc # 3; // String foo 5: invokevirtual # 4; // Méthode Java/io/PrintStream.println: (Ljava/lang/StrV 8: retourne Public void bar (); Code: 0: getstatic # 2; // Champ Java/lang/System.out: Ljava/io/PrintStream; 3: ldc # 5; // Barre de chaîne 5: invokevirtual # 4; // Méthode Java/io/PrintStream.println: (Ljava/lang/String;) V 8: getstatic # 2; // Field Java/lang/System.out: Ljava/io/PrintStream; 11: ldc # 5; // Barre de chaîne 13: invokevirtual # 4; // Méthode Java/io/PrintStream.println: (Ljava/lang/String;) V 16: retourne }
Quant à savoir pourquoi (Sun) ne donne pas d'avertissement à ce sujet, je n'en ai aucune idée :) Au moins le compilateur JDK a en fait DCE (Dead Code Elimination) intégré.
Le code inaccessible est une erreur selon le Java Language Spec .
Pour citer le JLS:
L'idée est qu'il doit exister un chemin d'exécution possible depuis le début du constructeur, de la méthode, de l'initialiseur d'instance ou de l'initialiseur statique qui contient l'instruction jusqu'à l'instruction elle-même. L'analyse prend en compte la structure des déclarations. À l'exception du traitement spécial de while, do et des instructions dont l'expression de condition a la valeur constante true, les valeurs des expressions ne sont pas prises en compte dans l'analyse de flux.
Cela signifie que le bloc if
n'est pas pris en compte, car si vous passez par l'un des chemins de l'instruction if
, vous pouvez atteindre l'instruction print finale. Si vous avez changé votre code pour être:
public void foo() {
System.out.println("Hello");
if (true)
return;
else
return;
System.out.println("World!");
}
puis soudain, il ne compilerait plus, car il n'y a pas de chemin à travers l'instruction if
qui permettrait d'atteindre la dernière ligne.
Autrement dit, un compilateur compatible Java compatible n'est pas autorisé à compiler votre premier fragment de code. Pour citer davantage le JLS:
Par exemple, l'instruction suivante entraîne une erreur de compilation:
while (false) { x=3; }
parce que l'énoncé x = 3; n'est pas accessible; mais le cas superficiellement similaire:
if (false) { x=3; }
n'entraîne pas d'erreur de compilation. Un compilateur d'optimisation peut se rendre compte que l'instruction x = 3; ne sera jamais exécuté et peut choisir d'omettre le code de cette instruction dans le fichier de classe généré, mais l'instruction x = 3; n'est pas considéré comme "inaccessible" au sens technique spécifié ici.
Le deuxième avertissement qu'Eclipse donne, à propos du code mort, est un avertissement généré par le compilateur, qui n'est pas "inaccessible", selon le JLS, mais en pratique l'est. Il s'agit d'une vérification de style lint supplémentaire fournie par Eclipse. Ceci est entièrement facultatif et, en utilisant la configuration Eclipse, peut être désactivé ou transformé en une erreur de compilation au lieu d'un avertissement.
Ce deuxième bloc est une "odeur de code", les blocs if (false)
sont normalement placés pour désactiver le code à des fins de débogage, le laisser derrière est généralement accidentel, et donc l'avertissement.
En fait, Eclipse effectue des tests encore plus avancés pour déterminer les valeurs possibles d'une instruction if afin de déterminer s'il est possible ou non d'emprunter les deux chemins. Par exemple, Eclipse se plaindrait également de code mort dans la méthode suivante:
public void foo() {
System.out.println("Hello");
boolean bool = Random.nextBoolean();
if (bool)
return;
if (bool || Random.nextBoolean())
System.out.println("World!");
}
Il générera un code inaccessible pour la seconde instruction if, car cela peut faire que bool
ne doit être false
qu'à ce stade du code. Dans un fragment de code aussi court, il est évident que les deux instructions if testent la même chose, mais s'il y a 10-15 lignes de code au milieu, cela pourrait ne plus être aussi évident.
Donc en résumé, la différence entre les deux: l'un est interdit par le JLS, et l'autre ne l'est pas, mais est détecté par Eclipse en tant que service au programmeur.
C'est pour permettre une sorte de compilation conditionnelle .
Ce n'est pas une erreur avec if
, mais le compilateur signalera une erreur pour while
, do-while
et for
.
C'est acceptable:
if (true) return; // or false
System.out.println("doing something");
Ce sont des erreurs
while (true) {
}
System.out.println("unreachable");
while (false) {
System.out.println("unreachable");
}
do {
} while (true);
System.out.println("unreachable");
for(;;) {
}
System.out.println("unreachable");
Il est expliqué à la fin de JLS 14.21: Déclarations inaccessibles :
La justification de ce traitement différent est de permettre aux programmeurs de définir des "variables de drapeau" telles que:
static final boolean DEBUG = false;
puis écrivez du code tel que:
if (DEBUG) { x=3; }
L'idée est qu'il devrait être possible de changer la valeur de DEBUG de false en true ou de true en false, puis de compiler le code correctement sans aucune autre modification du texte du programme.
La if (true)
est un peu plus subtile que "inaccessible"; car ce return
codé en dur rendra toujours le code suivant inaccessible, mais la modification de la condition dans le if
pourrait rendre l'instruction suivante accessible.
Avoir un conditionnel signifie qu'il y a un risque que la condition puisse changer. Il y a des cas où quelque chose de plus compliqué qu'un true
est entre parenthèses, et il n'est pas évident pour le lecteur humain que le code suivant est "amorti", mais le compilateur le remarque, donc il est capable de vous en avertir .
Eclipse est mentionné ici, et cela rend les choses un peu plus compliquées pour l'utilisateur; mais en fait sous Eclipse est juste un (très sophistiqué) Java qui comporte de nombreux commutateurs pour les avertissements, etc. qu'Eclipse peut activer et désactiver. En d'autres termes, vous ne le faites pas obtenir un large éventail d'avertissements/erreurs différents à partir d'une compilation directe javac
, et vous n'avez pas non plus de moyens pratiques pour les activer ou les désactiver tous. Mais c'est la même chose, juste avec plus de cloches et de sifflets.
Je pense qu'une façon de le faire est que le code inaccessible est probablement une erreur, et le JLS essaie de vous protéger contre de telles erreurs.
Autoriser if (true) return;
est un bon moyen de contourner la limitation JLS si vous voulez réellement le faire exprès. Si le JLS arrêtait cela, cela gênerait. En outre, il devrait également cesser:
public static boolean DEBUG = true; //In some global class somewhere else
...
if (DEBUG) return; //in a completely unrelated class.
...
Parce que la constante DEBUG est complètement intégrée et fonctionnellement équivalente à simplement taper un vrai dans cette condition if. Du point de vue JLS, ces deux cas sont très similaires.
Si vous souhaitez ignorer l'avertissement "avertissement de code mort dans Java sous Eclipse", procédez comme suit dans Eclipse *:
Enregistrez et fermez votre Eclipse IDE Lorsque vous rouvrez Eclipse, ces avertissements spécifiques ne devraient plus être répertoriés.
* Pour cet exemple de solution, j'utilise Eclipse IDE for Java Developers - Version: Mars.2 Release (4.5.2)
La différence réside dans la sémantique entre l'exécution et la compilation. Dans votre deuxième exemple, le code se compile en une branche if-else dans le bytecode, et Eclipse est simplement assez intelligent pour vous dire que la partie else ne sera jamais atteinte en runtime. Eclipse vous avertit uniquement, car il s'agit toujours d'un code légal.
Dans votre premier exemple, c'est une erreur car le code est illégal par la définition de Java. Le compilateur ne vous permet pas de créer du code octet avec des instructions inaccessibles.
J'ai fait quelques essais sur Eclipse et je pense qu'il existe 3 types de gestion de code mort de JDK: 1) pas d'avertissement, 2) avertissement et 3) erreur.
Pour un code de compilation conditionnelle "IF" typique, JDK le détecte et ne le signale pas comme un code mort. Pour un code mort provoqué par un indicateur booléen constant, JDK le détecte et le signale au niveau d'avertissement. Pour le code mort provoqué par le flux de contrôle du programme, JDK le détecte comme une erreur.
Voici mon essai:
public class Setting {
public static final boolean FianlDebugFlag = false;
}
class B {
.....
// no warn, it is typical "IF" conditional compilataion code
if(Setting.FianlDebugFlag)
System.out.println("am i dead?");
if(false)
System.out.println("am i dead?");
// warn, as the dead code is caused by a constant boolean flag
if(ret!=null && Setting.FianlDebugFlag)
System.out.println("am i dead?");
if(Setting.FinalDebug)
return null;
System.out.println("am i dea?");
// error, as the dead code is due to the program's control flow
return null;
System.out.println("am i dead");
}