Dlaczego w przełączniku Java nad opakowaniem liczb całkowitych przypadek „char” nie jest kompilowany, ale kompilacja jest poprawna, gdy przełącznik jest w stanie Byte?

18

Nie kompiluje:

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

Kompiluje OK:

void test(Byte x) {
      switch(x) {
          case 'a':
      }
}
Ali gh
źródło
1
Liczba całkowita to 4 bajty, a char to 2 bajty. Tak więc w pierwszym przypadku, bez względu na to, jaki znak napiszesz, jest on mniejszy niż liczba całkowita. W drugim przypadku napisany znak może być większy niż maksymalny bajt, dzięki czemu ta sprawa nigdy się nie wykona.
Jarosław Pawlak,
To wyjaśnienie jest nieprawidłowe. Rzeczywiście, w drugim przykładzie kod w 'a'przypadku zostanie wykonany w przypadku, który xjest bajtem 97. (Spróbuj, jeśli mi nie wierzysz). Prawdziwe wyjaśnienie znajduje się w mojej odpowiedzi.
Stephen C

Odpowiedzi:

19

Przyczyny są raczej skomplikowane, ale wszystkie są szczegółowo opisane ( drobnym drukiem, jeśli chcesz) specyfikacji języka Java.

Po pierwsze, JLS 14.11 mówi o switchoświadczeniach:

„Każda stała przypadku powiązana z instrukcją switch musi być przypisana zgodnie z typem wyrażenia instrukcji switch ( §5.2 ).”

Oznacza to, że 'a'należy go odpowiednio przypisać Integeri Byte .

Ale to nie brzmi dobrze:

  • Można by pomyśleć, że skoro 'a' należy przypisać do, Integerponieważ char-> int przypisanie jest legalne. (Każda charwartość zmieści się w int.)

  • Można by pomyśleć, że ponieważ 'a' NIE powinno się go przypisywać, Byteponieważ char-> byte przypisanie NIE jest legalne. (Większość charwartości nie mieści się w bajcie).

W rzeczywistości żadna z nich nie jest poprawna. Aby zrozumieć, dlaczego, musimy przeczytać, co JLS 5.2 faktycznie mówi o tym, co jest dozwolone w kontekstach przypisań.

„Konteksty przypisania umożliwiają użycie jednego z poniższych :

  • konwersja tożsamości (§5.1.1)
  • poszerzająca się prymitywna konwersja (pkt 5.1.2)
  • rozszerzenie konwersji odniesienia (pkt 5.1.5)
  • poszerzenie konwersji odniesienia, po której następuje rozpakowanie
  • konwersja odniesienia poszerzająca, po której następuje konwersja rozpakowywania, a następnie konwersja pierwotna poszerzająca
  • konwersja boksu (pkt 5.1.7)
  • konwersja boksu, po której następuje konwersja rozszerzającego odniesienia
  • konwersja rozpakowywania (pkt 5.1.8)
  • konwersja rozpakowywania, po której następuje poszerzanie pierwotnej konwersji ”.

Aby przejść od 'a'do Integer, musielibyśmy 1 poszerzyć charwartość do intpola a następnie intdo Integer. Ale jeśli spojrzysz na kombinacje dozwolonych konwersji, nie możesz wykonać rozszerzającej prymitywnej konwersji, po której następuje konwersja boksu.

Dlatego 'a'aby Integernie jest dozwolone. To wyjaśnia błąd kompilacji w pierwszym przypadku.

Można by pomyśleć, że 'a'do Bytejest niedozwolone, bo to wiązałoby się z prymitywnego zwężenie konwersji ... który nie ma na liście w ogóle. W rzeczywistości literały są szczególnym przypadkiem. JLS 5.2 mówi dalej.

„Ponadto, jeśli wyrażenie jest ciągłym wyrażeniem ( § 15.28 ) typu bajt, krótki, char lub int:

  • Zwężenie pierwotnej konwersji można zastosować, jeśli zmienna jest typu bajt, krótka lub char, a wartość stałego wyrażenia jest reprezentatywna dla typu zmiennej.

  • Zwężenie pierwotnej konwersji, po której następuje konwersja boksu, można zastosować, jeśli zmienna jest typu Byte, Shortlub Character, a wartość stałego wyrażenia jest reprezentowalna odpowiednio w bajcie typu, skrócie lub znaku. ”

Drugi z nich odnosi się do 'a'celu Byte, ponieważ:

  • dosłowny znak jest ciągłym wyrażeniem i
  • wartość 'a'jest 97dziesiętna, która mieści się w zakresie dla byte( -128do +127).

To wyjaśnia, dlaczego w drugim przykładzie nie ma błędu kompilacji.


1 - Nie możemy wstawić pola 'a'do a, Charactera następnie rozszerzyć Characterdo, Integerponieważ Characternie jest to podtyp Java Integer. Możesz użyć rozszerzającej konwersji odniesienia tylko wtedy, gdy typ źródła jest podtypem typu docelowego.

Stephen C.
źródło
Czy możemy użyć intjako typu przełącznika? (ponieważ char -> intdozwolone jest prymitywne poszerzenie)
AjahnCharles