Почему в Java при переключении через оболочку Integer регистр 'char' не компилируется, но компиляция в порядке, когда коммутатор находится над байтом?

18

Не компилируется:

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

Компилирует ОК:

void test(Byte x) {
      switch(x) {
          case 'a':
      }
}
али гх
источник
1
Целое число - 4 байта, а char - 2 байта. Так что в первом случае, независимо от того, какой символ вы пишете, он меньше целого числа. Однако во втором случае написанный вами символ может быть больше максимального байта, поэтому этот случай никогда не будет выполнен.
Ярослав Павляк
Это объяснение неверно. Действительно, во 2-м примере код в 'a'случае будет выполнен в случае, который xявляется байтом 97. (Попробуйте, если вы мне не верите.) Для реального объяснения смотрите мой ответ.
Стивен С.

Ответы:

19

Причины довольно сложны, но все они в деталях ( мелкий шрифт, если хотите) Спецификации языка Java.

Прежде всего, JLS 14.11 говорит следующее об switchутверждениях:

«Каждая константа регистра, связанная с оператором switch, должна соответствовать присваиванию с типом выражения оператора switch ( §5.2 )».

Это означает, что 'a'должно быть назначено Integerи Byte соответственно.

Но это не звучит правильно:

  • Можно было бы подумать , что так 'a' должно быть отнесено к Integerпотому , что char-> int назначение является законным. (Любое charзначение будет соответствовать int.)

  • Можно было бы подумать , что , поскольку 'a' не должно быть назначаемыми на Byteпотому , что char-> byte назначение не является законным. (Большинство charзначений не помещаются в байт.)

На самом деле, ни то, ни другое не является правильным. Чтобы понять почему, нам нужно прочитать, что на самом деле JLS 5.2 о том, что разрешено в контекстах присваивания.

«Контексты назначения позволяют использовать одно из следующего :

  • преобразование личности (§5.1.1)
  • расширяющееся примитивное преобразование (§5.1.2)
  • расширение эталонного преобразования (§5.1.5)
  • расширение эталонного преобразования с последующим распаковкой
  • расширение эталонного преобразования, за которым следует конвертирование без коробки, а затем конвертирование примитива с расширением
  • преобразование бокса (§5.1.7)
  • преобразование бокса с последующим расширением ссылки
  • распаковка конверсии (§5.1.8)
  • распаковка конверсии с последующим расширением примитивного преобразования. "

Чтобы перейти от 'a'к Integer, мы должны были бы 1 расширить charзначение на intто ЯЩИК intАнь Integer. Но если вы посмотрите на комбинации разрешенных преобразований, вы не сможете выполнить расширенное преобразование примитивов с последующим преобразованием в бокс.

Поэтому , 'a'чтобы Integerне допускается. Это объясняет ошибку компиляции в первом случае.

Можно подумать, что 'a'к Byteотвергается , потому что повлечет за собой примитивное сужающее преобразование ... , который не находится в списке вообще. На самом деле, литералы - это особый случай. JLS 5.2 продолжает говорить следующее.

«Кроме того, если выражение является константным выражением ( §15.28 ) типа byte, short, char или int:

  • Сужающее примитивное преобразование может использоваться, если переменная имеет тип byte, short или char, и значение константного выражения представимо в типе переменной.

  • Преобразование примитива сужения, сопровождаемое преобразованием бокса, может использоваться, если переменная имеет тип Byte, Shortили Character, и значение константного выражения может быть представлено в типе byte, short или char соответственно ".

Второй из них относится 'a'к Byte, потому что:

  • символьный литерал является константным выражением, и
  • значение 'a'является 97десятичным, которое находится в пределах диапазона от byte( -128до +127).

Это объясняет, почему нет ошибки компиляции во втором примере.


1 - Мы не можем боксировать 'a'к , Characterа затем расширить Characterдо , Integerпотому что Characterэто не Java подтип Integer. Вы можете использовать расширенное ссылочное преобразование, только если исходный тип является подтипом целевого типа.

Стивен С
источник
Можем ли мы использовать intв качестве типа переключателя? (поскольку char -> intдопускается примитивное расширение)
AjahnCharles