Comment utiliser null dans le commutateur

202
Integer i = ...

switch (i){
    case null:
        doSomething0();
        break;    
    }

Dans le code ci-dessus, je ne peux pas utiliser null dans l'instruction switch case. Comment puis-je procéder différemment? Je ne peux pas utiliser defaultcar je veux faire autre chose.

hudi
la source
9
avant la commutation, vérifiez la condition nulle si (i == null) {// dosomething}
Nagaraju Badaeni
8
Cela rendrait le commutateur utile. D'autres langues de correspondance de motifs fonctionnent de cette façon.
Pyrolistical

Réponses:

277

Ce n'est pas possible avec une switchinstruction en Java. Vérifiez nullavant switch:

if (i == null) {
    doSomething0();
} else {
    switch (i) {
    case 1:
        // ...
        break;
    }
}

Vous ne pouvez pas utiliser d'objets arbitraires dans les switchinstructions * . La raison pour laquelle le compilateur ne se plaint pas de l' switch (i)emplacement d' iun Integerest parce que Java décompresse automatiquement le fichier Integervers un fichier int. Comme l'a déjà dit assylias, le déballage va lancer un NullPointerExceptionquand iest null.

* Depuis Java 7 , vous pouvez utiliser Stringdans les switchdéclarations.

En savoir plus sur switch(y compris l'exemple avec une variable nulle) dans Oracle Docs - Switch

Jesper
la source
16
Vous pouvez également utiliser des énumérations dans les instructions switch.
joriki
27
Il est logique que vous ne puissiez pas utiliser un Integer nul ou une autre classe Wrapper, à cause du déballage. Mais qu'en est-il des énumérations et des chaînes? Pourquoi ne peuvent-ils pas être nuls?
Luan Nico
9
Je ne comprends pas pourquoi un court-circuit de null étant mappé au cas "par défaut" ou un cas spécial pour un commutateur nul n'a pas été implémenté pour les chaînes. Cela rend inutile l'utilisation de commutateurs pour simplifier le code car vous devez toujours effectuer une vérification nulle. Je ne dis pas que la simplification est la seule utilisation des commutateurs.
Reimius
3
@Reimius, vous n'avez pas toujours besoin de faire une vérification nulle. Si vous respectez les contrats de code que vous donnez à vos méthodes, vous pouvez presque toujours réussir à ne pas avoir votre code encombré de contrôles nuls. Cependant, utiliser des assertions est toujours agréable.
Joffrey
J'aimerais également connaître la réponse à la requête de @ LuanNico. Il semble déraisonnable que nullne peut pas être un cas valable lorsque vous travaillez avec Stringet enumtypes. Peut-être que l' enumimplémentation repose sur l'appel ordinal()en coulisses (même si, pourquoi ne pas traiter nullcomme ayant un `` ordinal '' de -1?), Et la Stringversion fait quelque chose en utilisant intern()et une comparaison de pointeurs (ou sinon s'appuie sur quelque chose qui nécessite strictement le déréférencement d'un objet)?
2016
98
switch ((i != null) ? i : DEFAULT_VALUE) {
        //...
}
tetsuo
la source
façon plus propre que d'en utiliser un de plus sinon
Vivek Agrawal
40

switch(i)lancera une NullPointerException si je le suis null, car il essaiera de déballer le Integerdans un int. Donc case null, ce qui se trouve être illégal, n'aurait jamais été atteint de toute façon.

Vous devez vérifier que i n'est pas nul avant l' switchinstruction.

assylias
la source
23

Les documents Java ont clairement indiqué que:

L'interdiction d'utiliser null comme étiquette de commutateur empêche d'écrire du code qui ne peut jamais être exécuté. Si l'expression de commutateur est d'un type de référence, tel qu'un type primitif encadré ou une énumération, une erreur d'exécution se produit si l'expression est évaluée à null au moment de l'exécution.

Vous devez vérifier la valeur null avant l'exécution de l'instruction Swithch.

if (i == null)

Voir la déclaration Switch

case null: // will never be executed, therefore disallowed.
Shehzad
la source
1
Les javadocs de votre lien ne disent plus "l'interdiction d'utiliser null comme étiquette de commutateur [etc.]".
Patrick M
14

Donné:

public enum PersonType {
    COOL_GUY(1),
    JERK(2);

    private final int typeId;
    private PersonType(int typeId) {
        this.typeId = typeId;
    }

    public final int getTypeId() {
        return typeId;
    }

    public static PersonType findByTypeId(int typeId) {
        for (PersonType type : values()) {
            if (type.typeId == typeId) {
                return type;
            }
        }
        return null;
    }
}

Pour moi, cela correspond généralement à une table de recherche dans une base de données (pour les tables rarement mises à jour uniquement).

Cependant, lorsque j'essaie d'utiliser findByTypeIddans une instruction switch (à partir, probablement, d'une entrée utilisateur) ...

int userInput = 3;
PersonType personType = PersonType.findByTypeId(userInput);
switch(personType) {
case COOL_GUY:
    // Do things only a cool guy would do.
    break;
case JERK:
    // Push back. Don't enable him.
    break;
default:
    // I don't know or care what to do with this mess.
}

... comme d'autres l'ont dit, cela se traduit par un NPE @ switch(personType) {. Une solution de contournement (c'est-à-dire «solution») que j'ai commencé à implémenter consistait à ajouter un UNKNOWN(-1)type.

public enum PersonType {
    UNKNOWN(-1),
    COOL_GUY(1),
    JERK(2);
    ...
    public static PersonType findByTypeId(int id) {
        ...
        return UNKNOWN;
    }
}

Désormais, vous n'avez plus à vérifier la valeur nulle là où cela compte et vous pouvez choisir de gérer ou non les UNKNOWNtypes. (REMARQUE: -1est un identifiant improbable dans un scénario commercial, mais choisissez évidemment quelque chose qui a du sens pour votre cas d'utilisation).

Beez
la source
2
UNKNOWNest la meilleure solution à ce sujet que j'aie jamais vue et surmonter les vérifications nulles.
membersound
5

Vous devez faire un

if (i == null) {
   doSomething0();
} else {
   switch (i) {
   }
}
Kai
la source
4

Certaines bibliothèques tentent de proposer des alternatives à l'instruction Java intégrée switch. Vavr est l'un d'entre eux, ils le généralisent à la correspondance de motifs.

Voici un exemple tiré de leur documentation :

String s = Match(i).of(
    Case($(1), "one"),
    Case($(2), "two"),
    Case($(), "?")
);

Vous pouvez utiliser n'importe quel prédicat, mais ils en offrent beaucoup et $(null)sont parfaitement légaux. Je trouve cela une solution plus élégante que les alternatives, mais cela nécessite java8 et une dépendance à la bibliothèque vavr ...

Emmanuel Touzery
la source
2

Vous pouvez également utiliser String.valueOf((Object) nullableString) comme

switch (String.valueOf((Object) nullableString)) {
case "someCase"
    //...
    break;
...
case "null": // or default:
    //...
        break;
}

Voir intéressant SO Q / A: Pourquoi String.valueOf (null) lève une NullPointerException

oikonomopo
la source
2
switch (String.valueOf(value)){
    case "null":
    default: 
}
l0v3
la source
0

Tu ne peux pas. Vous pouvez utiliser des primitives (int, char, short, byte) et String (Strings in java 7 only) dans switch. les primitives ne peuvent pas être nulles.
Vérifiez idans un état séparé avant de passer.

Mikita Belahlazau
la source
4
Vous pouvez également utiliser des énumérations.
Karu
5
si l'énumération est nulle, vous aurez le même problème. BTW, il est assez étrange que le commutateur ne puisse pas gérer null, car il a une clause par défaut
1
@LeonardoKenji La clause par défaut n'a vraiment rien à voir avec null; quoi que vous allumiez, il sera déréférencé afin de vérifier tous les autres cas, de sorte que la clause par défaut ne gérera pas le cas nul (une exception NullPointerException est levée avant qu'elle ne puisse le faire).
Ben
2
Je pense qu'il voulait dire que la clause par défaut devrait gérer null comme toute autre valeur d'énumération possible qui n'a pas été détectée par le cas précédent
Leo
0

Considérez simplement comment le SWITCH pourrait fonctionner,

  • en cas de primitives, nous savons que cela peut échouer avec NPE pour la boxe automatique
  • mais pour String ou enum , il peut s'agir de la méthode equals, qui a évidemment besoin d'une valeur LHS sur laquelle equals est invoqué. Donc, étant donné qu'aucune méthode ne peut être invoquée sur un null, le commutateur ne peut pas gérer null.
Puneet
la source
0

Basé sur la réponse @tetsuo, avec java 8:

Integer i = ...

switch (Optional.ofNullable(i).orElse(DEFAULT_VALUE)) {
    case DEFAULT_VALUE:
        doDefault();
        break;    
}
Calfater
la source