web-dev-qa-db-fra.com

IntelliJ se plaindre "pour l'instruction ne boucle pas"?

C'est le code que j'ai:

public enum Modification {
    NONE, SET, REMOVE;
}

boolean foo(){
    for (S s : sList) {
        final Modification modification = s.getModification();
        switch (modification) {
            case SET:
            case REMOVE:
                return true;
            /*
            case NONE:
                break;
            */
        }
    }
    return false;
}

Et quand le code sera comme ci-dessus, IntelliJ dira:

L'instruction 'for' ne fait pas moins de boucle ... () Indique toute instance d'instruction for, while et do dont le corps est garanti qu'il s'exécute au plus une fois. Normalement, ceci est une indication d'un bug.

IntelliJ sera heureux que si je fais le changement suivant:

for (S s : sList) {
    final Modification modification = s.getModification();
    switch (modification) {
        case SET:
        case REMOVE:
            return true;
        case NONE:
            break;
    }
}

Pourquoi ma boucle for ne boucle-t-elle pas si case NONE: n'est pas inclus dans l'instruction switch?

11
Koray Tugay

Je viens d'essayer cela dans Eclipse et vous vous retrouvez avec un avertissement du compilateur sur l'instruction switch.

La constante d'énumération NONE a besoin d'une étiquette de cas correspondante dans cette option d'énumération activée Modification

Pour résoudre l'avertissement, les options suivantes vous sont proposées.

  • Ajouter un cas par défaut
  • Ajouter les déclarations de cas manquantes
  • Ajoutez @suppressWarnings 'commutateur-incomplet' à foo ()

Si j'ajoute la déclaration de cas manquante, l'avertissement n'apparaît plus. La même chose que d'ajouter le cas manquant rend votre erreur avertissement disparaissent de intellij.

Sans la déclaration pour le cas NONE, vous ne pouvez voir que deux cas, qui retournent tous les deux la valeur true. Sans connaître la structure de Modification et la valeur supplémentaire de NONE, il semble que cette boucle ne retourne que vrai à la première itération de la boucle.

Bien sûr, le compilateur devrait savoir qu'il y a plus de valeurs pour Modification que SET et REMOVE, donc l'avertissement est juste pour un bon style. En gros, votre code fonctionne mais voici comment l’améliorer.

Je choisirais d'ajouter une déclaration par défaut plutôt que le cas manquant. Ce serait une meilleure preuve de l'avenir si plusieurs valeurs étaient ajoutées ultérieurement à l'énumération. PAR EXEMPLE.

switch (modification) 
{
  case SET:
  case REMOVE:
    return true;
  default:
    break;
}

Personnellement, je ne suis pas fan de l’utilisation des déclarations fall-switch. Ce que vous gagnez en rendant le code concis, vous perdez en lisibilité à mon humble avis. Si plus tard quelqu'un ajoute un casse entre SET et REMOVE, il pourrait introduire un bogue. De plus, le fait d'avoir une instruction return à mi-chemin d'une méthode peut également causer des problèmes. Si quelqu'un veut ajouter du code juste avant le retour, il risque de manquer tous les lieux. Si la méthode est très simple, les retours multiples sont acceptables, mais vous avez indiqué qu'il s'agissait d'un exemple simplifié. Par conséquent, si ce bloc de code est compliqué, je l'éviterais.

Si vous pouvez utiliser Java 8, cela semble être le cas d'utilisation idéal pour la nouvelle API de flux. Quelque chose comme ce qui suit devrait fonctionner.

return sList.stream().anyMatch(
  modification -> (modification==Modification.SET || modification==Modification.REMOVE)
);
7
Ben Thurley

Je dirais que c'est un faux positif.

1ère indication: Si vous exécutez votre code par le biais d'un débogueur - et que des éléments avec NONE modification ne figurent pas dans la liste avant un élément avec d'autres modifications - il se mettra en boucle.

2ème indication: Lorsque vous regardez le bytecode généré, il transforme l'instruction switch en (sorte de - ce n'est pas exactement identique)

for (S s : sList) {
    Modification modification = s.getModification();
        switch (modification.ordinal()) {
            case 1:
            case 2:
                return true;
    }
}

Si vous mettez cela dans votre code, IntelliJ ne se plaint pas. 

3ème indication: L'avertissement disparaît si vous mettez une déclaration supplémentaire avant le retour, c'est-à-dire System.out.println();

 switch (modification) {
     case SET:
     case REMOVE:
       System.out.println()
       return true;

On dirait que vous avez trompé l'inspection avec l'étiquette de cas manquant et que vous pourriez simplement ignorer l'avertissement.

2
Gerald Mücke

je suppose que ce sont vos trois seuls cas, n'est-ce pas? Donc, en gros, cela signifie que vous allez frapper l'un des deux premiers et renvoyer instantanément le message vrai, donc pas en boucle, ajoutez simplement un cas default et tout devrait bien fonctionner, c'est également une bonne pratique btw.

fondamentalement, il ne peut pas voir un cas où il ne retourne pas instantanément sans itérer la boucle

2
AngryDuck

Je pense que les inspections d'IntelliJ sont fausses. Je l'ai signalé à JetBrains

Edit: c'est corrigé

1
bwt

Votre boîtier de commutateur se casse ou revient toujours. Dans le premier cas, vous ne faites rien alias falls through. Le deuxième cas, returns, provoque l’arrêt du commutateur et de la boucle. Dans le troisième cas, vous break l'instruction switch qui provoque son arrêt. Il n'arrête cependant pas la boucle for (alias, il continue à itérer).

Ajoutez des fonctionnalités spécifiques pour le cas SET ou modifiez votre comportement sur les cas REMOVE et NONE.

public enum Modification {
    NONE, SET, REMOVE;
}

boolean foo(){
    for (S s : sList) {
        final Modification modification = s.getModification();
        switch (modification) {
            case SET:
               // This case falls through to the REMOVE case
            case REMOVE:
                return true; // This statement stops the switch, loop and returns true
            case NONE:
                break; // This statement stops the switch and continues the loop.
        }
    }
    return false;
}

Votre commutateur ne boucle pas sans la casse NONE car return interrompt la boucle et renvoie une valeur de la fonction. break interrompt la boucle mais continue la boucle for.


Sur demande de l'OP une explication supplémentaire.

Le fait de tomber à travers signifie que le cas suivant sera exécuté jusqu'à ce qu'un arrêt (break ou return) soit atteint. Cela rend l'équivalent des extraits de code suivants:

case SET:
case REMOVE:
    return true;

est le même que:

case SET:
    return true;
case REMOVE:
    return true;
0
Lars de Bruijn