Не компилируется:
void test(Integer x){
switch(x){
case 'a':
}
}
Компилируется нормально:
void test(Byte x){
switch(x){
case 'a':
}
}
Не компилируется:
void test(Integer x){
switch(x){
case 'a':
}
}
Компилируется нормально:
void test(Byte x){
switch(x){
case 'a':
}
}
Причины довольно сложные, но все они в деталях (мелкий шрифт, если хотите) в спецификации языка 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 продолжает говорить следующее.
"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.