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

Lesezeit: 7 Minuten

Benutzer-Avatar
Prashant Bhate

Ich frage mich nur, warum das Java 7 switch Aussage unterstützt nicht a null Fall und wirft stattdessen NullPointerException? Siehe die kommentierte Zeile unten (Beispiel aus den Java-Tutorials-Artikel auf 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 vermieden if Bedingung für Nullprüfung vor jedem switch verwenden.

  • Keine schlüssige Antwort darauf, da wir nicht die Leute sind, die die Sprache gemacht haben. Alle Antworten werden reine Vermutungen sein.

    – Sternchen

    15. August 2013 um 23:22 Uhr

  • Ein Versuch, einzuschalten null wird eine Ausnahme verursachen. Führen Sie ein if prüfen Auf nulldann gehen Sie in die switch Aussage.

    – gparjani

    15. August 2013 um 23:22 Uhr

  • Von dem JLS: Nach Einschätzung der Entwickler der Programmiersprache Java [throwing a NullPointerException if the expression evaluates to null at runtime] ist ein besseres Ergebnis, als die gesamte switch-Anweisung stillschweigend zu überspringen oder die Anweisungen (falls vorhanden) nach dem Standardlabel (falls vorhanden) auszuführen.

    – gparjani

    15. August 2013 um 23:37 Uhr

  • @gparyani: Mach das zu einer Antwort. Das klingt sehr offiziell und endgültig.

    – Thilo

    15. August 2013 um 23:42 Uhr

  • @JeffGohlke: “Es gibt keine Möglichkeit, eine Warum-Frage zu beantworten, es sei denn, Sie sind die Person, die die Entscheidung getroffen hat.”… nun, gparyanis Kommentar beweist das Gegenteil

    – Benutzer541686

    15. August 2013 um 23:51 Uhr


Benutzer-Avatar
Paul Bellora

Wie damryfbfnetsi in den Kommentaren betont, JLS §14.11 hat folgenden Hinweis:

Das Nutzungsverbot null als Switch-Label verhindert, dass Code geschrieben wird, der niemals ausgeführt werden kann. Wenn die switch Der Ausdruck ist vom Referenztyp, d. h. String oder ein eingerahmter primitiver Typ oder ein Aufzählungstyp, dann tritt ein Laufzeitfehler auf, wenn der Ausdruck zu ausgewertet wird null zur Laufzeit. Nach Ansicht der Entwickler der Programmiersprache Java ist dies ein besseres Ergebnis, als das Ganze stillschweigend zu überspringen switch Anweisung oder die Entscheidung, die Anweisungen (falls vorhanden) nach der auszuführen default Etikett (falls vorhanden).

(Hervorhebung von mir)

Während der letzte Satz die Möglichkeit der Verwendung überspringt case null:scheint vernünftig und bietet einen Einblick in die Absichten der Sprachdesigner.

Betrachten wir eher Implementierungsdetails, diesen Blogbeitrag von Christian Hujer hat einige aufschlussreiche Spekulationen darüber, warum null ist in Schaltern nicht erlaubt (obwohl es sich um die enum Schalter statt der String Schalter):

Unter der Haube die switch -Anweisung wird normalerweise in einen Tablesswitch-Bytecode kompiliert. Und das “physikalische” Argument dazu switch sowie seine Fälle sind ints. Der einzuschaltende Int-Wert wird durch Aufrufen der Methode ermittelt Enum.ordinal(). Das […] Ordnungszahlen beginnen bei Null.

Das bedeutet Kartierung null zu 0 wäre keine gute idee. Ein Schalter am ersten Enum-Wert wäre nicht von Null zu unterscheiden. Vielleicht wäre es eine gute Idee gewesen, die Ordnungszahlen für Aufzählungen bei 1 zu zählen. Dies ist jedoch nicht so definiert, und diese Definition kann nicht geändert werden.

Während String Schalter sind unterschiedlich implementiert, die enum Schalter kam zuerst und hat den Präzedenzfall dafür geschaffen, wie sich das Einschalten eines Referenztyps verhalten sollte, wenn die Referenz ist null.

  • Es wäre eine große Verbesserung gewesen, die Nullbehandlung als Teil von a zuzulassen case null: wenn es exklusiv für implementiert worden wäre String. Aktuell alle String Die Prüfung erfordert sowieso eine Nullprüfung, wenn wir es richtig machen wollen, wenn auch meistens implizit, indem die String-Konstante wie in vorangestellt wird "123test".equals(value). Jetzt sind wir gezwungen, unsere switch-Anweisung wie in zu schreiben if (value != null) switch (value) {...

    – JoJo

    4. Februar 2016 um 23:06 Uhr


  • Zu “Null auf 0 abbilden wäre keine gute Idee”: Das ist eine Untertreibung, da der Wert von “.hashcode() 0 ist! Dies würde bedeuten, dass ein Null-String und ein String der Länge Null in einer switch-Anweisung identisch behandelt werden müssten, was eindeutig nicht realisierbar ist.

    – Skomisa

    16. Dezember 2017 um 2:41 Uhr

  • Was hat sie im Fall von enum daran gehindert, null auf -1 abzubilden?

    – knackig

    18. September 2019 um 2:54 Uhr

Im Algemeinen null ist unangenehm zu handhaben; vielleicht kann eine bessere Sprache ohne leben null.

Ihr Problem könnte gelöst werden durch

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

    }

  • Keine gute Idee, wenn month ist ein leerer String: Dadurch wird er genauso behandelt wie ein Null-String.

    – gparjani

    16. August 2013 um 21:11 Uhr

  • In vielen Fällen kann es durchaus sinnvoll sein, null als leeren String zu behandeln

    – Eric Waldmeister

    6. April 2014 um 21:41 Uhr

Benutzer-Avatar
knackig

Es ist nicht schön, aber String.valueOf() ermöglicht es Ihnen, einen Null-String in einem Schalter zu verwenden. Wenn es findet nulles wandelt es um "null", andernfalls gibt es nur denselben String zurück, den Sie ihm übergeben haben. Wenn Sie nicht behandeln "null" explizit, dann wird es gehen default. Die einzige Einschränkung ist, dass es keine Möglichkeit gibt, zwischen dem String zu unterscheiden "null" und eine tatsächliche null Variable.

    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;

  • Ich glaube, so etwas in Java zu tun, ist ein Antimuster.

    – Lukasz Rzeszotarski

    30. März 2018 um 5:59 Uhr

  • @ŁukaszRzeszotarski Das ist ziemlich genau das, was ich mit “es ist nicht schön” gemeint habe

    – knackig

    6. April 2018 um 14:05 Uhr


Benutzer-Avatar
Prashant Bhate

Dies ist ein Versuch zu beantworten, warum es wirft NullPointerException

Die Ausgabe des Javap-Befehls unten zeigt dies case wird basierend auf dem Hashcode der ausgewählt switch Argumentzeichenfolge und wirft daher NPE, wenn .hashCode() wird auf Null-String aufgerufen.

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

Das bedeutet, basierend auf Antworten auf Kann Javas hashCode denselben Wert für verschiedene Zeichenfolgen erzeugen?, obwohl selten, besteht immer noch die Möglichkeit, dass zwei Fälle übereinstimmen (zwei Zeichenfolgen mit demselben Hash-Code). Siehe dieses Beispiel unten

    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 wofür

  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

Wie Sie sehen, wird nur ein Fall generiert "Ea" und "FB" aber mit zwei if Bedingungen, um auf eine Übereinstimmung mit jeder Groß-/Kleinschreibung zu prüfen. Sehr interessante und komplizierte Art, diese Funktionalität zu implementieren!

Benutzer-Avatar
stätig

Lange Rede kurzer Sinn … (und hoffentlich interessant genug!!!)

Enum wurden zuerst in eingeführt Java1.5 (September 2004) und die Insekt auffordern zu Einschalten des Strings zulassen wurde vor langer Zeit abgelegt (Okt’95). Wenn Sie sich den Kommentar ansehen, der zu diesem Fehler gepostet wurde Juni 2004es sagt Don't hold your breath. Nothing resembling this is in our plans. Sieht so aus, als hätten sie aufgeschoben (ignoriert) diesen Fehler und starteten schließlich Java 1.5 im selben Jahr, in dem sie ‘enum’ mit einer Ordnungszahl beginnend bei 0 einführten und entschieden (verpasst), um null für enum nicht zu unterstützen. Später im Java1.7 (Juli 2011) Sie folgten (gezwungen) dieselbe Philosophie wie bei String (dh beim Generieren des Bytecodes wurde keine Nullprüfung durchgeführt, bevor die Methode hashcode() aufgerufen wurde).

Ich denke also, es läuft darauf hinaus, dass enum zuerst hereinkam und mit seinem ordinalen Beginn bei 0 implementiert wurde, aufgrund dessen sie keinen Nullwert im Switch-Block unterstützen konnten und später mit String beschlossen, dieselbe Philosophie zu erzwingen, dh Nullwert nicht im Schalterblock erlaubt.

TL;DR Mit String hätten sie sich um NPE kümmern können (verursacht durch den Versuch, Hashcode für null zu generieren), während sie die Java-Code-zu-Byte-Code-Konvertierung implementierten, sich aber schließlich dagegen entschieden.

Ref:
Der Käfer,
JavaVersionHistory,
JavaCodeToByteCodeALSO

Benutzer-Avatar
BlackHatSamurai

Laut Java-Docs:

Ein Schalter arbeitet mit den primitiven Datentypen byte, short, char und int. Es funktioniert auch mit Aufzählungstypen (diskutiert in Enum-Typen), der String-Klasse und einigen speziellen Klassen, die bestimmte primitive Typen umhüllen: Character, Byte, Short und Integer (diskutiert in Zahlen und Strings).

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

Benutzer-Avatar
Amrith

Die Antwort lautet einfach: Wenn Sie einen Schalter mit einem Referenztyp (z. B. einem geschachtelten primitiven Typ) verwenden, tritt der Laufzeitfehler auf, wenn der Ausdruck null ist, da das Unboxing die NPE auslösen würde.

case null (was illegal ist) könnte also sowieso nie ausgeführt werden 😉

  • Das hätte man aber auch anders umsetzen können.

    – Thilo

    15. August 2013 um 23:40 Uhr

  • OK @Thilo, an dieser Umsetzung waren klügere Leute als ich beteiligt. Wenn Sie andere Möglichkeiten kennen, wie dies hätte implementiert werden können, würde ich gerne wissen, welche das sind [and I’m sure there are others] also teile…

    – Amrith

    16. August 2013 um 2:02 Uhr

  • Zeichenfolgen sind keine geschachtelten primitiven Typen, und die NPE tritt nicht auf, weil jemand versucht, sie zu „entpacken“.

    – Thilo

    16. August 2013 um 2:15 Uhr

  • @thilo, die anderen Möglichkeiten, dies zu implementieren, sind, was?

    – Amrith

    16. August 2013 um 10:30 Uhr

  • if (x == null) { // the case: null part }

    – Thilo

    19. August 2013 um 12:56 Uhr

1338350cookie-checkWarum unterstützt die String switch-Anweisung keinen Nullfall?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy