为什么在Java通过Integer包装器进行切换时,不编译'char'大小写,但是在通过字节切换时编译就可以了吗?


18

无法编译:

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

编译OK:

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

1
整数是4个字节,而char是2个字节。因此,在第一种情况下,无论您写什么字符,它都小于整数。但是,在第二种情况下,您写入的字符可能大于最大字节,从而使该情况永远不会执行。
Jaroslaw Pawlak,

这种解释是不正确的。确实,在第二个示例中,该'a'情况下的代码x为byte 的情况下执行97。(如果您不相信我,请尝试。)有关真正的解释,请参阅我的答案。
斯蒂芬·C

Answers:


19

原因很复杂,但是原因全在Java语言规范的细节中(如果需要,可以打印精美)。

首先,JLS 14.11说了以下about switch语句:

“与switch语句相关联的每种情况常量都必须与switch语句的Expression(第5.2节)的类型兼容。”

这意味着'a'需要 分别分配给IntegerByte

但这听起来不对:

  • 您会认为,因为'a' 应该可以 since 分配给一个,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,然后框intInteger。但是,如果您查看允许的转换组合,则无法进行加宽原始转换和装箱转换。

因此'a'Integer是不允许的。这解释了第一种情况下的编译错误。

您会认为'a'to Byte是不被允许的,因为这将涉及原始的缩小转换...根本不在列表中。实际上,文字是一种特殊情况。 JLS 5.2继续说以下内容。

“此外,如果该表达式是类型为byte,short,char或int 的常量表达式(第15.28节):

  • 如果变量的类型为byte,short或char,并且常量表达式的值可以表示为变量的类型,则可以使用缩窄的原始转换。

  • 如果变量的类型为ByteShortCharacter,并且常量表达式的值分别以字节,short或char类型表示,则可以使用紧缩原始转换和装箱转换。

其中的第二个适用'a'Byte,因为:

  • 字符文字是一个常量表达式,并且
  • 的值'a'97十进制,在byte-128+127)的范围内。

这解释了为什么第二个示例中没有编译错误。


1-我们不能'a'将a 装箱Character,然后再扩大Character到,Integer因为Character这不是Java的Java子类型Integer。如果源类型是目标类型的子类型,则只能使用加宽引用转换。


我们可以int用作开关类型吗?(因为char -> int允许原始加宽)
AjahnCharles
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.