Je me demande simplement pourquoi l'instruction Java 7 switch
ne prend pas en charge un cas null
et renvoie plutôt NullPointerException
? Voir la ligne commentée ci-dessous (exemple tiré de l'article sur les tutoriels Java sur switch
):
{
String month = null;
switch (month) {
case "january":
monthNumber = 1;
break;
case "february":
monthNumber = 2;
break;
case "march":
monthNumber = 3;
break;
//case null:
default:
monthNumber = 0;
break;
}
return monthNumber;
}
Cela aurait évité une condition if
pour la vérification de la nullité avant chaque utilisation de switch
.
Comme damryfbfnetsi souligne dans les commentaires, JLS §14.11 a la note suivante:
L'interdiction d'utiliser
null
comme étiquette de commutateur empêche toute personne d'écrire du code qui ne peut jamais être exécuté. Si l'expressionswitch
est d'un type référence, c'est-à-direString
ou d'un type primitif encadré ou d'un type enum, une erreur d'exécution se produira si l'expression est évaluée ànull
au moment de l'exécution. Selon les concepteurs du langage de programmation Java, il s'agit d'un meilleur résultat que de sauter en silence l'instructionswitch
entière ou de choisir d'exécuter les instructions (le cas échéant) après l'étiquettedefault
(le cas échéant).
(c'est moi qui souligne)
Bien que la dernière phrase évite la possibilité d'utiliser case null:
, elle semble raisonnable et offre un aperçu des intentions des concepteurs de langage.
Si nous examinons plutôt les détails de la mise en œuvre, cet article de blog de Christian Hujer émet des hypothèses éclairantes sur la raison pour laquelle null
n'est pas autorisé dans les commutateurs (bien qu'il soit centré sur le commutateur enum
plutôt que sur le commutateur String
):
Sous le capot, l'instruction
switch
sera généralement compilée en un code d'octet tablesswitch. Et l'argument "physique" àswitch
ainsi que ses cas sontint
s. La valeur int à activer est déterminée en appelant la méthodeEnum.ordinal()
. Les ordinaux [...] commencent à zéro.Cela signifie que mapper
null
à0
ne serait pas une bonne idée. Un commutateur sur la première valeur enum serait indiscernable de null. Cela aurait peut-être été une bonne idée de commencer à compter les ordinaux pour des enums de 1. Cependant, cela n'a pas été défini ainsi et cette définition ne peut pas être modifiée.
Alors que String
commutateurs sont implémentés différemment , le commutateur enum
est arrivé en premier et a défini le précédent de la manière dont l'activation d'un type de référence doit se comporter lorsque la référence est null
.
En général, null
est difficile à manipuler; peut-être qu'une meilleure langue peut vivre sans null
.
Votre problème pourrait être résolu par
switch(month==null?"":month)
{
...
//case "":
default:
monthNumber = 0;
}
Ce n'est pas beau, mais String.valueOf()
vous permet d'utiliser une chaîne nulle dans un commutateur. S'il trouve null
, il le convertit en "null"
, sinon il renvoie simplement la même chaîne que celle que vous avez transmise. Si vous ne gérez pas "null"
explicitement, il passera à default
. Le seul inconvénient est qu'il n'y a aucun moyen de faire la distinction entre String "null"
et une variable réelle null
.
String month = null;
switch (String.valueOf(month)) {
case "january":
monthNumber = 1;
break;
case "february":
monthNumber = 2;
break;
case "march":
monthNumber = 3;
break;
case "null":
monthNumber = -1;
break;
default:
monthNumber = 0;
break;
}
return monthNumber;
NullPointerException
La sortie de la commande javap ci-dessous révèle que case
est choisi en fonction du hashcode de la chaîne d'argument switch
et renvoie donc NPE lorsque .hashCode()
est appelé sur une chaîne nulle.
6: invokevirtual #18 // Method Java/lang/String.hashCode:()I
9: lookupswitch { // 3
-1826660246: 44
-263893086: 56
103666243: 68
default: 95
}
Cela signifie basé sur les réponses à Le hashCode de Java peut-il produire la même valeur pour différentes chaînes? bien que rare, il est toujours possible que deux cas soient mis en correspondance (deux chaînes avec le même code de hachage) Voir cet ex ci-dessous
int monthNumber;
String month = args[0];
switch (month) {
case "Ea":
monthNumber = 1;
break;
case "FB":
monthNumber = 2;
break;
// case null:
default:
monthNumber = 0;
break;
}
System.out.println(monthNumber);
javap pour lequel
10: lookupswitch { // 1
2236: 28
default: 59
}
28: aload_3
29: ldc #22 // String Ea
31: invokevirtual #24 // Method Java/lang/String.equals:(Ljava/lang/Object;)Z
34: ifne 49
37: aload_3
38: ldc #28 // String FB
40: invokevirtual #24 // Method Java/lang/String.equals:(Ljava/lang/Object;)Z
43: ifne 54
46: goto 59 //Default
ainsi que vous pouvez voir qu’un seul cas est généré, mais avec deux si condition pour vérifier mach avec chaque chaîne de cas. Moyen très intéressant et compliqué d’implémenter cette fonctionnalité!
Longue histoire courte ... (et j'espère assez intéressant !!!)
Enum ont été introduits pour la première fois dans Java1.5 (Sep'2004) et le bug demandant à autoriser le basculement sur String a été archivé depuis longtemps (Oct '95). Si vous regardez le commentaire posté sur ce bogue à Jun'2004, il indique Don't hold your breath. Nothing resembling this is in our plans.
On dirait qu'ils ont différé (ignoré) ce bogue et ont finalement lancé Java 1.5 la même année où ils ont introduit 'enum 'avec l'ordinal commençant à 0 et ayant décidé (raté) de ne pas prendre en charge null pour enum. Plus tard, dans Java1.7 (Jul'2011), ils ont suivi (forcé) la même philosophie que String (c.-à-d. Lors de la génération du bytecode, aucune vérification nulle n'a été effectuée avant d'appeler hashcode () méthode).
Donc, je pense que cela revient au fait que l'énumération est arrivée en premier et a été implémentée avec son ordinal commençant à 0, raison pour laquelle ils ne pouvaient pas supporter la valeur null dans le bloc switch, et plus tard avec String, ils ont décidé d'imposer la même philosophie autorisé dans le bloc de commutation.
TL; DR Avec String, ils pourraient s’occuper de NPE (suite à une tentative de génération de hashcode pour null) lors de l’implémentation du code Java pour la conversion du code octet, mais ont finalement décidé de ne pas le faire.
Réf: TheBUG , JavaVersionHistory , JavaCodeToByteCode , SO
Selon Java Docs:
Un commutateur fonctionne avec les données primitives octet, short, char et int les types. Cela fonctionne aussi avec les types énumérés (discutés dans Types Enum), la classe String et quelques classes spéciales qui enveloppent certaines types primitifs: Character, Byte, Short et Integer (voir dans Nombres et Chaînes).
Puisque null
n'a pas de type et n'est pas une instance de rien, cela ne fonctionnera pas avec une instruction switch.
La réponse est simplement que si vous utilisez un commutateur avec un type de référence (tel qu'un type primitif en boîte), l'erreur d'exécution se produira si l'expression est nulle, car unboxing enverrait le NPE.
donc case null (ce qui est illégal) ne pourra jamais être exécuté de toute façon;)
Je suis d'accord avec les commentaires perspicaces (Sous le capot ....) dans https://stackoverflow.com/a/18263594/1053496 dans la réponse de @Paul Bellora.
J'ai trouvé une raison de plus de mon expérience.
Si 'case' peut être null, cela signifie que switch (variable) est nul, tant que le développeur fournit un cas de correspondance 'null' correspondant, nous pourrons alors affirmer que tout va bien. Mais que se passera-t-il si le développeur ne fournit aucun cas "null" correspondant? Ensuite, nous devons le faire correspondre à un cas "par défaut" qui peut ne pas correspondre à ce que le développeur avait l'intention de gérer dans le cas par défaut. Par conséquent, la correspondance de "null" avec une valeur par défaut pourrait provoquer un "comportement surprenant" ... ". Par conséquent, le fait de lancer" NPE "obligera le développeur à gérer explicitement tous les cas. J'ai trouvé que jeter des NPE dans ce cas était très réfléchi.
Utilisez la classe Apache StringUtils
String month = null;
switch (StringUtils.trimToEmpty(month)) {
case "xyz":
monthNumber=1;
break;
default:
monthNumber=0;
break;
}