Warum wird bei einem Java-Switch über einen Integer-Wrapper ein 'char'-Fall nicht kompiliert, aber die Kompilierung ist in Ordnung, wenn der Switch über Byte erfolgt?

18

Kompiliert nicht:

void test(Integer x) {
      switch (x) {
          case 'a':
      }
}

Kompiliert OK:

void test(Byte x) {
      switch(x) {
          case 'a':
      }
}
ali gh
quelle
1
Ganzzahl ist 4 Bytes, während Zeichen 2 Bytes sind. Im ersten Fall ist das Zeichen, egal welches Sie schreiben, kleiner als die Ganzzahl. Im zweiten Fall kann das von Ihnen geschriebene Zeichen jedoch größer als das maximale Byte sein, sodass dieser Fall niemals ausgeführt wird.
Jaroslaw Pawlak
Diese Erklärung ist falsch. Tatsächlich im 2. Beispiel - Code im 'a'Fall wird in dem Fall ausgeführt werden , dass xdas Byte 97. (Versuchen Sie es, wenn Sie mir nicht glauben.) Die wirkliche Erklärung finden Sie in meiner Antwort.
Stephen C

Antworten:

19

Die Gründe sind ziemlich kompliziert, aber sie liegen alle in den Details ( Kleingedrucktes, wenn Sie möchten) der Java-Sprachspezifikation.

Zunächst sagt der JLS 14.11 Folgendes über switchAussagen:

"Jede der switch-Anweisung zugeordnete Fallkonstante muss mit dem Typ des Ausdrucks der switch-Anweisung kompatibel sein ( §5.2 )."

Dies bedeutet , dass 'a'zuordenbar sein muss Integerund Byte jeweils.

Das klingt aber nicht richtig:

  • Sie würden denken, dass da 'a' einem zuweisbar sein sollte, Integerweil char-> int Zuordnung legal ist. (Jeder charWert passt in eine int.)

  • Sie würden denken, dass da 'a' NICHT einer zuweisbar sein sollte, Byteweil char-> byte Zuordnung NICHT legal ist. (Die meisten charWerte passen nicht in ein Byte.)

In der Tat ist keines davon richtig. Um zu verstehen, warum, müssen wir lesen, was JLS 5.2 tatsächlich über das erlaubt, was in Zuweisungskontexten zulässig ist.

"Zuweisungskontexte ermöglichen die Verwendung einer der folgenden Optionen :

  • eine Identitätsumwandlung (§5.1.1)
  • eine sich erweiternde primitive Umwandlung (§5.1.2)
  • eine erweiterte Referenzumwandlung (§5.1.5)
  • eine erweiterte Referenzkonvertierung, gefolgt von einer Unboxing-Konvertierung
  • eine sich erweiternde Referenzumwandlung, gefolgt von einer Unboxing-Umwandlung, gefolgt von einer sich erweiternden primitiven Umwandlung
  • eine Boxumwandlung (§5.1.7)
  • eine Boxumwandlung, gefolgt von einer erweiterten Referenzumwandlung
  • eine Unboxing-Konvertierung (§5.1.8)
  • eine Unboxing-Konvertierung, gefolgt von einer sich erweiternden primitiven Konvertierung. "

Um von zu 'a'zu gelangen Integer, müssten wir den Wert auf 1 erweitern charund intdann intauf einen Wert setzen Integer. Wenn Sie sich jedoch die zulässigen Kombinationen von Conversions ansehen, können Sie keine erweiterte primitive Conversion gefolgt von einer Box-Conversion durchführen.

Daher ist 'a'to Integernicht erlaubt. Dies erklärt den Kompilierungsfehler im ersten Fall.

Sie würden denken, dass 'a'to Bytenicht erlaubt ist, da dies eine primitive Verengungskonvertierung beinhalten würde ... die überhaupt nicht in der Liste enthalten ist. In der Tat sind Literale ein Sonderfall. JLS 5.2 führt Folgendes aus.

"Wenn der Ausdruck ein konstanter Ausdruck ( §15.28 ) vom Typ Byte, Short, Char oder Int ist:

  • Eine sich verengende primitive Konvertierung kann verwendet werden, wenn die Variable vom Typ Byte, Short oder Char ist und der Wert des konstanten Ausdrucks im Typ der Variablen darstellbar ist.

  • Eine verengende primitive Konvertierung, gefolgt von einer Boxkonvertierung, kann verwendet werden, wenn die Variable vom Typ Byte, Shortoder Characterist und der Wert des konstanten Ausdrucks im Typbyte, Kurzschluss oder Zeichen darstellbar ist. "

Die zweite davon gilt 'a'für Byte, weil:

  • ein Zeichenliteral ist ein konstanter Ausdruck, und
  • Der Wert von 'a'ist 97dezimal und liegt innerhalb des Bereichs für byte( -128bis +127).

Dies erklärt, warum im zweiten Beispiel kein Kompilierungsfehler vorliegt.


1 - Wir können nicht Box 'a'auf ein Characterund dann erweitern Characterauf , Integerweil Characterkein Java - Subtyp Integer. Sie können eine erweiterte Referenzkonvertierung nur verwenden, wenn der Quelltyp ein Subtyp des Zieltyps ist.

Stephen C.
quelle
Können wir intals Schaltertyp verwenden? (da char -> intist primitive Erweiterung, die erlaubt ist)
AjahnCharles