我在Eclipse中编写了以下代码:
byte b = 10;
/* some other operations */
b = ~b;
Eclipse希望在逐位补码的行中强制转换为字节。上面写着:"类型不匹配:无法从int转换为byte"。我还在其他位运算和其他积分类型上尝试过这种方法。它与short和char相同。只有long和integer可以使用按位运算。
这是有原因的吗?
~
)和二元运算符分别对其操作数进行"一元数字提升"(JLS,第5.6.2节)和"二元数字提升(JLS)",这是"首先将事物提升到至少int
"的奇特术语。
具体来说,对于一元数字促销,引用上面链接的JLS部分:
某些运算符将一元数字提升应用于单个操作数,该操作数必须生成数字类型的值:
和
如果操作数是编译时类型的byte、short或char,则通过加宽基元转换将其提升为int类型的值(§5.1.2)。
(二进制数字提升类似,对两个操作数都进行操作。)
因此,即使b
是byte
,~b
也是int
,因为b
的值首先被提升为int
。
解决方案:将其投射回byte
:
b = (byte) (~b);
为什么,Java
这就留下了一个问题,为什么?对于我能找到的操作员来说,在byte
s、short
s和char
s上操作的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
s和long
s找到XOR(以及其他一元和二进制运算符)的指令(在适当的情况下,其他运算符存在float
和double
版本)。
因此,Java必须执行这些提升,因为没有用于在byte
s、short
s或char
s上执行这些操作的字节码指令。
为什么不呢,JVM
这就引出了另一个问题:为什么JVM不支持这样的字节码指令?答案似乎是,"因为在一个字节的指令集中编码它们太多了。"根据JVM规范,第2.11.1节,
考虑到Java虚拟机的单字节操作码大小,将类型编码为操作码会给其指令集的设计带来压力。如果每个类型化的指令都支持Java虚拟机的所有运行时数据类型,则指令数量将超过一个字节所能表示的数量。相反,Java虚拟机的指令集为某些操作提供了较低级别的类型支持。换句话说,指令集是有意不正交的。根据需要,可以使用单独的指令在不支持和支持的数据类型之间进行转换。
(重点矿井)
总之,JVM的一个字节码指令集排除了byte
s、char
s和short
s上大多数操作的字节码指令,因此需要一元数字提升和二进制数字提升。
是的,小于int
的类型在用作大多数运算符的操作数时会进行提升。它们首先被有效地强制转换为int
。因此,在上面的代码中,结果的类型是int
。有关详细信息,请参阅JLS的本节。
至于为什么Java会这样做,我不确定。但一个合理的原因是C会这样做,而保持熟悉的语义可能是语言设计的目标。