Question 1:
Pourquoi le code suivant se compile sans avoir de déclaration de retour?
public int a() {
while(true);
}
Remarque: si j'ajoute un retour après un certain temps, j'obtiens un Unreachable Code Error
.
Question 2:
D'un autre côté, pourquoi le code suivant se compile-t-il,
public int a() {
while(0 == 0);
}
même si ce qui suit ne le fait pas.
public int a(int b) {
while(b == b);
}
java
syntax
while-loop
compilation
return
Willi Mentzel
la source
la source
Réponses:
Ceci est couvert par JLS§8.4.7 :
Puisque le compilateur sait que la boucle ne se terminera jamais (
true
c'est toujours vrai, bien sûr), il sait que la fonction ne peut pas "retourner normalement" (déposer la fin de son corps), et donc il est normal qu'il n'y en ait pasreturn
.Dans ce
0 == 0
cas, le compilateur sait que la boucle ne se terminera jamais (ce0 == 0
sera toujours vrai). Mais il ne le sait pas pourb == b
.Pourquoi pas?
Le compilateur comprend les expressions constantes (§15.28) . Citant le §15.2 - Formes d'expressions (car étrangement cette phrase n'est pas dans le §15.28) :
Dans votre
b == b
exemple, comme une variable est impliquée, ce n'est pas une expression constante et n'est pas spécifiée pour être déterminée au moment de la compilation. Nous pouvons voir que cela va toujours être vrai dans ce cas (bien que sib
c'était undouble
, comme l'a souligné QBrute , nous pourrions facilement être dupésDouble.NaN
, ce qui n'est pas==
lui-même ), mais le JLS spécifie uniquement que les expressions constantes sont déterminées au moment de la compilation , il ne permet pas au compilateur d'essayer d'évaluer des expressions non constantes. bayou.io a soulevé un bon point pour pourquoi pas: si vous commencez à essayer de déterminer des expressions impliquant des variables au moment de la compilation, où vous arrêtez-vous?b == b
est évident (euh, pour lesNaN
valeurs), mais qu'en est-ila + b == b + a
? Ou(a + b) * 2 == a * 2 + b * 2
? Tracer la ligne aux constantes est logique.Donc, comme il ne "détermine" pas l'expression, le compilateur ne sait pas que la boucle ne se terminera jamais, il pense donc que la méthode peut retourner normalement - ce qu'il n'est pas autorisé à faire, car il est nécessaire d'utiliser
return
. Il se plaint donc de l'absence d'unreturn
.la source
Il peut être intéressant de penser à un type de retour de méthode non pas comme une promesse de retourner une valeur du type spécifié, mais comme une promesse de ne pas retourner une valeur qui n'est pas du type spécifié. Ainsi, si vous ne retournez jamais rien, vous ne violez pas la promesse, et donc l'un des éléments suivants est légal:
Boucler pour toujours:
Récursif pour toujours:
Lever une exception:
(Je trouve la récursion amusante à penser: le compilateur pense que la méthode retournera une valeur de type
X
(quelle qu'elle soit), mais ce n'est pas vrai, car il n'y a pas de code présent qui ait une idée de la façon de créer ou procurer unX
.)la source
En regardant le code d'octet, si ce qui est retourné ne correspond pas à la définition, vous recevrez une erreur de compilation.
Exemple:
for(;;)
affichera les bytecodes:Notez l'absence de tout bytecode de retour
Cela ne frappe jamais un retour et ne retourne donc pas le mauvais type.
A titre de comparaison, une méthode comme:
Renvoie les bytecodes suivants:
Notez le "areturn" qui signifie "retourner une référence"
Maintenant, si nous faisons ce qui suit:
Renvoie les bytecodes suivants:
Nous pouvons maintenant voir que le type dans la définition ne correspond pas au type de retour d'ireturn, ce qui signifie return int.
Donc, vraiment, cela revient à dire que si la méthode a un chemin de retour, ce chemin doit correspondre au type de retour. Mais il y a des cas dans le bytecode où aucun chemin de retour n'est généré du tout, et donc aucune violation de la règle.
la source