为什么在 Java 切换整数包装器时,“char”情况是否无法编译,但是当切换超过 Byte 时编译是可以的?

2022-09-02 21:42:38

不编译:

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'IntegerByte

但这听起来不对:

  • 您可能会认为,因为应该可以分配给一个因为->赋值是合法的。(任何值都适合 .)'a'Integercharintcharint

  • 您可能会认为,因为不应该分配给一个因为->分配是不合法的。(大多数值不适合字节。'a'Bytecharbytechar

事实上,这些都不正确。为了理解为什么,我们需要阅读JLS 5.2实际上关于赋值上下文中允许的内容。

“工作分配上下文允许使用下列方法之一

  • 身份转换 (§5.1.1)
  • 加宽基元转换 (§5.1.2)
  • 加宽参考转换 (§5.1.5)
  • 加宽参考转换,然后进行拆箱转换
  • 加宽参考转换,然后进行取消装箱转换,然后进行加宽基元转换
  • 拳击转换 (§5.1.7)
  • 装箱转换后进行加宽参考转换
  • 拆箱转换 (§5.1.8)
  • 拆箱转换,然后加宽基元转换。

要从 到 ,我们需要将值扩大 1 到 一个 然后 框 到 一个 .但是,如果您查看允许的转换组合,则无法先进行加宽基元转换,然后再进行装箱转换。'a'IntegercharintintInteger

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

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

“此外,如果表达式是字节、短、字符或 int 类型的常量表达式 (§15.28):

  • 如果变量的类型为 byte、short 或 char,并且常量表达式的值可在变量的类型中表示,则可以使用窄基元转换。

  • 如果变量的类型为 Byte、Short 或 Character,并且常量表达式的值分别以 byteshort 或 char 类型表示,则可以使用缩小基元转换,然后进行装箱转换。

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

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

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


1 - 我们不能将“a”框入字符,然后将字符扩大到整数,因为字符不是整数的Java子类型。仅当源类型是目标类型的子类型时,才能使用加宽引用转换。


答案 2