我在Eclipse中编写了以下代码:
byte b = 10;
/* some other operations */
b = ~b;
Eclipse在按位取反的行中需要将类型转换为byte。它报错说:“类型不匹配:无法从int转换为byte”。我还尝试了其他按位运算和其他整数类型,但对于short和char也是一样的。只有long和integer可以使用按位运算。
这是为什么呢?
我在Eclipse中编写了以下代码:
byte b = 10;
/* some other operations */
b = ~b;
Eclipse在按位取反的行中需要将类型转换为byte。它报错说:“类型不匹配:无法从int转换为byte”。我还尝试了其他按位运算和其他整数类型,但对于short和char也是一样的。只有long和integer可以使用按位运算。
这是为什么呢?
Java中的一元(例如~
)和二元运算符分别对其操作数进行"一元数值提升"(JLS,第5.6.1节)和"二元数值提升"(JLS,第5.6.2节),这些术语指的是“首先将事物提升至至少int
类型”。
具体而言,在一元数值提升中,引用上述链接的JLS部分:
某些运算符对单个操作数应用一元数值提升,该操作数必须产生数值类型的值:
和
......如果操作数的编译时类型为byte、short或char,则通过扩展原始转换(§5.1.2)将其升级为int类型的值。
(二元数值提升类似,作用于两个操作数。)
所以,即使b是一个byte,~b
也是一个int,因为b的值首先被升级为int。解决方案:将其强制转换回byte:b = (byte) (~b);
为什么选择Java?
那么问题来了,为什么选择Java呢?似乎对于我能找到的运算符,操作byte
、short
和char
类型的JVM字节码指令根本不存在。例如,你正在使用的单目位补运算符(~)被实现为与-1
(所有位均设置)进行"XOR"操作。从这个链接中可以看出:
tempSpock &= ~mask;
变成
25 iload_2 // Push local variable 2 (mask).
26 iconst_m1 // Push -1.
27 ixor // Bitwise EXCLUSIVE-OR top two ints: ~mask
int
和long
的XOR指令的说明(以及其他一元和二元运算符),(其他运算符的float
和double
版本也存在)。byte
、short
或char
上执行这些操作。
JVM为什么不支持这样的字节码指令呢?
这带来了另一个问题:为什么JVM不支持这样的字节码指令?答案似乎是“因为在一个字节的指令集中编码所有这些指令将会太多”。根据JVM规范第2.11.1节的说法,int
小的类型在大多数操作符中使用时会经历 提升。它们被有效地转换为 int
。因此,在你上面的代码中,结果的类型是 int
。详见 JLS 的这个章节。
至于 Java 为什么这样做,我不确定。但一个合理的原因可能是 C 这样做了,并且保持熟悉的语义可能是一项语言设计目标。