J'ai rencontré une situation dans laquelle une méthode non void manque une déclaration de retour et le code se compile toujours. Je sais que les instructions après la boucle while sont inaccessibles (code mort) et ne seraient jamais exécutées. Mais pourquoi le compilateur ne prévient-il même pas de retourner quelque chose? Ou pourquoi un langage nous permettrait-il d'avoir une méthode non vide ayant une boucle infinie et ne renvoyant rien?
public int doNotReturnAnything() {
while(true) {
//do something
}
//no return statement
}
Si j'ajoute une instruction break (même conditionnelle) dans la boucle while, le compilateur se plaint des erreurs infâmes: Method does not return a value
dans Eclipse et Not all code paths return a value
dans Visual Studio.
public int doNotReturnAnything() {
while(true) {
if(mustReturn) break;
//do something
}
//no return statement
}
Cela est vrai à la fois pour Java et C #.
unreachable statement
s'il y a quelque chose après la boucle.Réponses:
La règle pour les méthodes non-void est que chaque chemin de code qui retourne doit renvoyer une valeur , et cette règle est satisfaite dans votre programme: zéro chemin de code sur zéro qui retourne renvoient une valeur. La règle n'est pas "chaque méthode non-void doit avoir un chemin de code qui retourne".
Cela vous permet d'écrire des méthodes de stub comme:
IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
C'est une méthode non nulle. Il doit s'agir d'une méthode non vide pour satisfaire l'interface. Mais il semble ridicule de rendre cette implémentation illégale car elle ne renvoie rien.
Que votre méthode ait un point final inaccessible à cause d'un
goto
(rappelez-vous, awhile(true)
est juste une manière plus agréable d'écriregoto
) au lieu d'unthrow
(qui est une autre forme degoto
) n'est pas pertinent.Parce que le compilateur n'a aucune bonne preuve que le code est incorrect. Quelqu'un a écrit
while(true)
et il semble probable que la personne qui a fait cela savait ce qu'elle faisait.Voir mes articles sur le sujet, ici:
ATBG: accessibilité de facto et de jure
Et vous pouvez également envisager de lire la spécification C #.
la source
public int doNotReturnAnything() {
boolean flag = true;
while (flag) {
//Do something
}
//no return
}
Ce code n'a pas non plus de point d'arrêt. Alors maintenant, comment le compilateur sait que le code est faux.if
,while
,for
,switch
, etc, constructions qui fonctionnent sur ramification constantes sont traitées comme des branches sans conditions par le compilateur. La définition exacte de l' expression constante est dans la spécification. Les réponses à vos questions se trouvent dans le cahier des charges; mon conseil est que vous le lisez.Every code path that returns must return a value
Meilleure réponse. Répond clairement aux deux questions. MerciLe compilateur Java est suffisamment intelligent pour trouver le code inaccessible (le code après
while
boucle)et comme il est inaccessible , il ne sert à rien d'y ajouter une
return
déclaration (après lawhile
fin)il en va de même avec le conditionnel
if
public int get() { if(someBoolean) { return 10; } else { return 5; } // there is no need of say, return 11 here; }
étant donné que la condition booléenne ne
someBoolean
peut être évaluée qu'à l'untrue
ou l' autrefalse
, il n'est pas nécessaire de fournir un aprèsreturn
explicitementif-else
, car ce code est inaccessible et Java ne s'en plaint pas.la source
return
instruction dans un code inaccessible, mais n'a rien à voir avec la raison pour laquelle vous n'avez besoin d' aucunereturn
instruction dans le code de l'OP.public int doNotReturnAnything() {
if(true){
System.exit(1);
}
return 11;
}
public int doNotReturnAnything() {
while(true){
System.exit(1);
}
return 11;// Compiler error: unreachable code
}
System.exit(1)
va tuer le programme. Il ne peut détecter que certains types de code inaccessible.System.exit(1)
va tuer le programme, nous pouvons en utiliser n'importe lequelreturn statement
, maintenant le compilateur reconnaît lereturn statements
. Et le comportement est le même, le retour exige pourif condition
mais pas pourwhile
.while(true)
Le compilateur sait que la
while
boucle n'arrêtera jamais de s'exécuter, donc la méthode ne se terminera jamais, donc unereturn
instruction n'est pas nécessaire.la source
Étant donné que votre boucle s'exécute sur une constante - le compilateur sait que c'est une boucle infinie - ce qui signifie que la méthode ne pourra jamais revenir, de toute façon.
Si vous utilisez une variable, le compilateur appliquera la règle:
Cela ne compilera pas:
// Define other methods and classes here public int doNotReturnAnything() { var x = true; while(x == true) { //do something } //no return statement - won't compile }
la source
La spécification Java définit un concept appelé
Unreachable statements
. Vous n'êtes pas autorisé à avoir une instruction inaccessible dans votre code (c'est une erreur de compilation). Vous n'êtes même pas autorisé à avoir une déclaration de retour après le while (true); déclaration en Java. Unewhile(true);
instruction rend les instructions suivantes inaccessibles par définition, vous n'avez donc pas besoin d'unereturn
instruction.Notez que bien que le problème d'arrêt soit indécidable dans le cas générique, la définition de l'instruction inaccessible est plus stricte que simplement l'arrêt. C'est décider des cas très spécifiques où un programme ne s'arrête définitivement pas. Le compilateur n'est théoriquement pas capable de détecter toutes les boucles infinies et les instructions inaccessibles mais il doit détecter les cas spécifiques définis dans la spécification (par exemple, le
while(true)
cas)la source
Le compilateur est suffisamment intelligent pour découvrir que votre
while
boucle est infinie.Le compilateur ne peut donc pas penser à votre place. Il ne peut pas deviner pourquoi vous avez écrit ce code. Même chose pour les valeurs de retour des méthodes. Java ne se plaindra pas si vous ne faites rien avec les valeurs de retour de la méthode.
Donc, pour répondre à votre question:
Le compilateur analyse votre code et après avoir découvert qu'aucun chemin d'exécution ne conduit à tomber la fin de la fonction, il se termine par OK.
Il peut y avoir des raisons légitimes à une boucle infinie. Par exemple, de nombreuses applications utilisent une boucle principale infinie. Un autre exemple est un serveur Web qui peut attendre indéfiniment des demandes.
la source
Dans la théorie des types, il y a quelque chose appelé le type inférieur qui est une sous-classe de tous les autres types (!) Et est utilisé pour indiquer la non-terminaison entre autres. (Les exceptions peuvent compter comme un type de non-résiliation - vous ne vous arrêtez pas via le chemin normal.)
Donc, d'un point de vue théorique, ces instructions qui ne se terminent pas peuvent être considérées comme retournant quelque chose de type Bottom, qui est un sous-type de int, donc vous obtenez (en quelque sorte) votre valeur de retour après tout du point de vue du type. Et c'est parfaitement normal que cela n'a aucun sens qu'un type puisse être une sous-classe de tout le reste, y compris int, car vous n'en retournez jamais un.
Dans tous les cas, via la théorie des types explicites ou non, les compilateurs (rédacteurs de compilateurs) reconnaissent que demander une valeur de retour après une instruction sans fin est idiot: il n'y a aucun cas possible dans lequel vous pourriez avoir besoin de cette valeur. (Cela peut être bien que votre compilateur vous avertisse quand il sait que quelque chose ne se terminera pas mais il semble que vous vouliez qu'il renvoie quelque chose. Mais c'est mieux de laisser les vérificateurs de style à la lint, car vous avez peut-être besoin de la signature de type qui pour une autre raison (par exemple, sous-classification) mais vous voulez vraiment la non-terminaison.)
la source
Il n'y a pas de situation dans laquelle la fonction peut atteindre sa fin sans renvoyer une valeur appropriée. Par conséquent, le compilateur n'a rien à redire.
la source
Visual studio a le moteur intelligent pour détecter si vous avez tapé un type de retour, alors il devrait avoir une instruction return avec dans la fonction / méthode.
Comme en PHP Votre type de retour est vrai si vous n'avez rien retourné. le compilateur obtient 1 si rien n'est retourné.
À partir de ce
public int doNotReturnAnything() { while(true) { //do something } //no return statement }
Le compilateur sait que l'instruction while elle-même a une nature infinie afin de ne pas la considérer. et le compilateur php deviendra automatiquement vrai si vous écrivez une condition dans l'expression de while.
Mais pas dans le cas de VS il vous renverra une erreur dans la pile.
la source
Votre boucle while fonctionnera pour toujours et par conséquent ne sortira pas pendant; il continuera à s'exécuter. Par conséquent, la partie extérieure de while {} est inaccessible et il n'y a pas de raison d'écrire return ou non. Le compilateur est suffisamment intelligent pour déterminer quelle partie est accessible et quelle partie ne l'est pas.
Exemple:
public int xyz(){ boolean x=true; while(x==true){ // do something } // no return statement }
Le code ci-dessus ne se compilera pas, car il peut y avoir un cas où la valeur de la variable x est modifiée dans le corps de la boucle while. Cela rend donc la partie extérieure de la boucle while accessible! Et par conséquent, le compilateur lancera une erreur «aucune instruction de retour trouvée».
Le compilateur n'est pas assez intelligent (ou plutôt paresseux;)) pour déterminer si la valeur de x sera modifiée ou non. J'espère que cela efface tout.
la source
int x = 1; while(x * 0 == 0) { ... }
s'agissait d'une boucle infinie, mais la spécification dit que le compilateur ne doit faire des déductions de flux de contrôle que lorsque l'expression de boucle est constante et que la spécification définit une expression constante comme ne contenant aucune variable . Le compilateur était donc trop intelligent . En C # 3, j'ai fait en sorte que le compilateur corresponde à la spécification, et depuis lors, il est délibérément moins intelligent à propos de ce type d'expressions."Pourquoi le compilateur ne prévient-il même pas de renvoyer quelque chose? Ou pourquoi un langage nous permettrait-il d'avoir une méthode non vide ayant une boucle infinie et ne renvoyant rien?".
Ce code est également valable dans toutes les autres langues (probablement sauf Haskell!). Parce que la première hypothèse est que nous écrivons «intentionnellement» du code.
Et il y a des situations où ce code peut être totalement valide comme si vous allez l'utiliser comme thread; ou s'il retournait a
Task<int>
, vous pourriez effectuer une vérification d'erreur basée sur la valeur int retournée - qui ne devrait pas être retournée.la source
Je me trompe peut-être mais certains débogueurs permettent la modification des variables. Ici, alors que x n'est pas modifié par le code et qu'il sera optimisé par JIT, on pourrait modifier x en false et la méthode devrait retourner quelque chose (si une telle chose est autorisée par le débogueur C #).
la source
Les spécificités du cas Java pour cela (qui sont probablement très similaires au cas C #) sont liées à la façon dont le compilateur Java détermine si une méthode est capable de retourner.
Plus précisément, les règles sont qu'une méthode avec un type de retour ne doit pas pouvoir se terminer normalement et doit à la place toujours se terminer brusquement (brusquement ici indiquant via une instruction de retour ou une exception) selon JLS 8.4.7 .
Le compilateur cherche à voir si une terminaison normale est possible sur la base des règles définies dans JLS 14.21 Instructions inaccessibles car il définit également les règles pour la complétion normale.
Notamment, les règles pour les instructions inaccessibles font un cas spécial uniquement pour les boucles qui ont une
true
expression constante définie :Donc, si l'
while
instruction peut se terminer normalement , une instruction return ci-dessous est nécessaire car le code est considéré comme accessible, et toutewhile
boucle sans instruction break ou constante accessibletrue
expression est considérée comme capable de se terminer normalement.Ces règles signifient que votre
while
instruction avec une expression vraie constante et sans abreak
n'est jamais considérée comme terminée normalement , et donc tout code en dessous n'est jamais considéré comme accessible . La fin de la méthode est en dessous de la boucle, et comme tout ce qui se trouve en dessous de la boucle est inaccessible, la fin de la méthode l'est aussi, et donc la méthode ne peut pas se terminer normalement (ce que recherche le complicateur).if
les instructions, en revanche, ne bénéficient pas de l'exemption spéciale concernant les expressions constantes accordées aux boucles.Comparer:
// I have a compiler error! public boolean testReturn() { final boolean condition = true; if (condition) return true; }
Avec:
// I compile just fine! public boolean testReturn() { final boolean condition = true; while (condition) { return true; } }
La raison de la distinction est assez intéressante, et est due au désir de permettre des indicateurs de compilation conditionnelle qui ne provoquent pas d'erreurs de compilation (à partir du JLS):
Pourquoi l'instruction de rupture conditionnelle entraîne-t-elle une erreur du compilateur?
Comme indiqué dans les règles d'accessibilité de boucle, une boucle while peut également se terminer normalement si elle contient une instruction break accessible. Étant donné que les règles de joignabilité d'une
if
de déclaration alors l' article ne prend pas l'état duif
compte du tout, une telle conditionif
déclaration est alors clause est toujours considérée comme atteignable.Si le
break
est accessible, le code après la boucle est à nouveau considéré comme accessible. Puisqu'il n'y a pas de code accessible qui entraîne une interruption brutale après la boucle, la méthode est alors considérée comme capable de se terminer normalement, et donc le compilateur la signale comme une erreur.la source