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

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

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

Компилируется нормально:

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

Ответ 1

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

Во-первых, JLS 14.11 говорит следующее о заявлениях switch:

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

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

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

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

  • Можно подумать, что поскольку 'a' НЕ следует назначать на Byte, потому что назначение charbyte НЕ допустимо. (Большинство значений 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 продолжает говорить следующее.

"In addition, if the expression is a constant expression (§15.28) of type byte, short, char, or int:

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

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

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

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

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


1 - We can't box [TG435] to a [TG436] and then widen [TG437] to [TG438] because [TG439] is not a Java subtype of [TG440]. You can only use a widening reference conversion if the source type is a subtype of the target type.