it-swarm.com.de

Warum unterstützt die String switch-Anweisung keinen Nullfall?

Ich frage mich nur, warum die Java 7-Anweisung switch keinen Fall null unterstützt und stattdessen NullPointerException wirft. Siehe die kommentierte Zeile unten (Beispiel aus dem Java-Tutorials-Artikel zu 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;
}

Dies hätte eine if-Bedingung für die Nullprüfung vor jeder switch-Verwendung vermieden.

108
Prashant Bhate

Wie damryfbfnetsi weist darauf hin in den Kommentaren, hat JLS §14.11 die folgende Anmerkung:

Das Verbot der Verwendung von null als Schalterlabel verhindert, dass Code geschrieben wird, der niemals ausgeführt werden kann. Wenn der Ausdruck switch von einem Referenztyp ist, d. H. String oder einem geschachtelten Grundelementtyp oder einem Aufzählungstyp, tritt ein Laufzeitfehler auf, wenn der Ausdruck zur Laufzeit null wertet. Nach Meinung der Entwickler der Java-Programmiersprache ist dies ein besseres Ergebnis, als die gesamte switch-Anweisung stillschweigend zu überspringen oder die Anweisungen (falls vorhanden) nach dem default-Label (falls vorhanden) auszuführen.

(Hervorhebung meines)

Während der letzte Satz die Möglichkeit der Verwendung von case null: überspringt, erscheint er sinnvoll und bietet einen Einblick in die Absichten der Sprachdesigner.

Wenn wir uns die Implementierungsdetails ansehen, hat dieser Blogbeitrag von Christian Hujer einige aufschlussreiche Spekulationen darüber, warum null in Switches nicht zulässig ist (obwohl es sich eher um den Switch enum als um den Switch String handelt):

Unter der Haube wird die Anweisung switch normalerweise zu einem Bytecode für Tablesswitches kompiliert. Das "physikalische" Argument für switch sowie seine Fälle sind ints. Der int-Wert zum Einschalten wird durch Aufrufen der Methode Enum.ordinal() bestimmt. Die Ordinalzahlen beginnen bei Null.

Das bedeutet, null auf 0 abzubilden, wäre keine gute Idee. Ein Einschalten des ersten Aufzählungswerts wäre nicht von Null zu unterscheiden. Vielleicht wäre es eine gute Idee gewesen, die Ordinalzahlen für Aufzählungen mit 1 zu zählen. Dies wurde jedoch nicht so definiert, und diese Definition kann nicht geändert werden.

Während String-Schalter unterschiedlich implementiert sind , kam der enum-Schalter an erster Stelle und legt den Präzedenzfall für das Verhalten eines Referenztyps fest, wenn die Referenz null ist.

134
Paul Bellora

Im Allgemeinen ist null bösartig in der Handhabung; vielleicht kann eine bessere Sprache ohne null leben.

Ihr Problem könnte durch gelöst werden

    switch(month==null?"":month)
    {
        ...
        //case "":
        default: 
            monthNumber = 0;

    }
25
ZhongYu

Es ist nicht schön, aber mit String.valueOf() können Sie einen Null-String in einem Switch verwenden. Wenn null gefunden wird, konvertiert es es in "null". Wenn Sie "null" nicht explizit behandeln, wird default aufgerufen. Der einzige Nachteil ist, dass es keine Möglichkeit gibt, zwischen dem String "null" und einer tatsächlichen Variable null zu unterscheiden. 

    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;
23
krispy

Dies ist ein Versuch zu beantworten, warum es NullPointerException wirft

Die Ausgabe des javap-Befehls unten zeigt, dass case auf der Grundlage des Hashcodes der switch-Argumentzeichenfolge ausgewählt wird und daher NPE auslöst, wenn .hashCode() für die Nullzeichenfolge aufgerufen wird. 

6: invokevirtual #18                 // Method Java/lang/String.hashCode:()I
9: lookupswitch  { // 3
    -1826660246: 44
     -263893086: 56
      103666243: 68
        default: 95
   }

Dies bedeutet, basierend auf Antworten auf Kann der Hash-Code von Java denselben Wert für verschiedene Zeichenfolgen erzeugen? Obwohl es selten vorkommt, besteht immer noch die Möglichkeit, dass zwei Fälle übereinstimmen (zwei Zeichenfolgen mit demselben Hash-Code)

    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 für was

  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

gut wie Sie sehen können, wird nur ein Fall generiert, aber mit zwei if-Bedingungen, um mach mit jeder Fallzeichenfolge zu prüfen. Sehr interessante und komplizierte Implementierung dieser Funktionalität!

14
Prashant Bhate

Lange Geschichte kurz ... (und hoffentlich interessant genug !!!)

Enum wurde zuerst in Java1.5 (Sep'2004) eingeführt und der Bug - Befehl an allow auf String wurde lange zurück abgelegt (Oct'95 ). Wenn Sie sich den Kommentar zu diesem Fehler bei Jun'2004 ansehen, heißt es, Don't hold your breath. Nothing resembling this is in our plans. Sieht so aus, als ob sie diesen Fehler verschoben (ignoriert) und schließlich Java 1.5 in demselben Jahr gestartet haben, in dem sie eingeführt wurden 'enum' mit Ordinal beginnend bei 0 und entschied (missed), null für enum nicht zu unterstützen. Später in Java1.7 (Jul'2011) folgten sie (forced) derselben Philosophie wie String (dh, während der Bytecode generiert wurde, wurde vor dem Aufruf von hascode () keine Nullprüfung durchgeführt.) Methode).

Ich denke, es läuft darauf hinaus, dass die Aufzählung zuerst eingeführt wurde und mit einer Ordinalzahl von 0 implementiert wurde, aufgrund derer sie keinen Nullwert im Schalterblock unterstützen konnten. Später beschlossen sie mit String, die gleiche Philosophie zu erzwingen, dh Nullwert nicht im Schalterblock erlaubt.

TL; DR Mit String konnten sie sich bei der Implementierung von Java-Code in Byte-Code-Konvertierung um NPE kümmern (verursacht durch den Versuch, Hashcode für Null zu generieren), entschlossen sich jedoch nicht dazu.

Ref: TheBUG , JavaVersionHistory , JavaCodeToByteCode , SO

4
sactiw

Laut Java Docs:

Ein Switch arbeitet mit den Byte-, Short-, Char- und Int-Primitivdaten Typen. Es funktioniert auch mit Aufzählungstypen (wird in Aufzählungstypen beschrieben) die String-Klasse und einige spezielle Klassen, die bestimmte .__-Elemente umschließen. Primitivtypen: Character, Byte, Short und Integer (in Numbers and Strings beschrieben).

Da null keinen Typ hat und keine Instanz von irgendetwas ist, funktioniert sie nicht mit einer switch-Anweisung.

1
BlackHatSamurai

Die Antwort ist einfach, dass der Laufzeitfehler auftritt, wenn Sie einen Switch mit einem Referenztyp (z. B. einem geschachtelten Grundelementtyp) verwenden, wenn der Ausdruck NULL ist, da durch das Unboxing die NPE ausgelöst wird.

der Fall null (der illegal ist) könnte also sowieso nie ausgeführt werden;)

0
amrith

Ich bin mit aufschlussreichen Kommentaren (Unter der Haube ...) in https://stackoverflow.com/a/18263594/1053496 in der Antwort von @Paul Bellora einverstanden. 

Ich habe aus meiner Erfahrung einen weiteren Grund gefunden.

Wenn 'case' null sein kann, bedeutet dies, dass switch (variable) null ist. Solange der Entwickler einen übereinstimmenden 'null'-Fall bereitstellt, können wir argumentieren, dass dies in Ordnung ist. Was passiert jedoch, wenn der Entwickler keinen übereinstimmenden 'null'-Fall bereitstellt. Dann müssen wir es mit einem "Standard" -Fall abgleichen, der möglicherweise nicht von dem beabsichtigt ist, den der Entwickler im Standardfall behandeln möchte. Daher kann das Übereinstimmen von 'null' mit einem Standardwert 'überraschendes Verhalten' bewirken. __ Wenn Sie also 'NPE' werfen, wird der Entwickler dazu gezwungen, alle Fälle explizit zu behandeln. Das Werfen von NPE fand ich in diesem Fall sehr nachdenklich.

0
nantitv

Verwenden Sie die Apache StringUtils-Klasse

String month = null;
switch (StringUtils.trimToEmpty(month)) {
    case "xyz":
        monthNumber=1;  
    break;
    default:
       monthNumber=0;
    break;
}
0
bhagat