Java:为什么我会收到错误消息“类型不匹配:无法将int转换为byte”?

13

如果你声明类型为byte或short的变量,并尝试对它们执行算术运算,你会收到错误信息“Type mismatch: cannot convert int to short”(或相应地“Type mismatch: cannot convert int to byte”)。

byte a = 23;
byte b = 34;
byte c = a + b;

在这个示例中,编译错误出现在第三行。


1
如果我没记错,JVM 会将字节和短整型存储为整数,因此通常使用这两种数据类型几乎没有什么好处。当然,我意识到你正在做比你的示例更复杂的事情,可能有非常好的理由。 - David Webb
5个回答

12
虽然算术运算符被定义为可用于任何数字类型,但根据Java语言规范(5.6.2二进制数值提升),类型为byte和short的操作数在被传递给运算符之前会自动升级为int。
要对类型为byte或short的变量执行算术操作,必须将表达式括在括号内(其中操作将作为类型int执行),然后将结果转换回所需的类型。
例如:
byte a = 23;
byte b = 34;
byte c = (byte) (a + b);
这里有一个关于真正的Java大师的后续问题:为什么?类型byte和short是完全可以的数字类型。为什么Java不允许直接对这些类型进行算术运算?(答案不是"精度损失",因为首先没有明显的理由将其转换为int。)
更新:jrudolph建议,这种行为是基于JVM中可用的操作,具体来说,只实现了全字和双字运算符。因此,要对字节和短整数进行操作,它们必须转换为int。

我猜这是由于性能原因。在某些 CPU 中,使用比 CPU 寄存器更小的操作数可能会更昂贵。可能甚至会使用 32 位 int 来存储字节和短整型,以使它们在内存中保持对齐。 - jassuncao
自从我发现操作是在CPU寄存器中进行的,但是是的,它们是32位对齐的。并且你也不能对字符执行+运算符。 - bestsss
在转换后,表达式byte c = (byte)(a + b)将变成byte c = 57,其中57是byte类型。但是,由于数值提升规定所有的byte、short和char字面量都会被转换为int,因此这个byte 57会被提升为int 57。所以从这个意义上讲,编译器应该仍然会报错,但它没有。为什么呢? - user12208242

7
你的后续问题的答案在这里:
操作数为byte和short类型时,会在交给运算符之前自动提升为int类型。
因此,在你的例子中,a和b都被转换为int类型,然后交给+运算符。两个int相加的结果也是int类型。试图将该int分配给byte值会导致错误,因为存在精度损失的可能性。通过显式地强制转换结果,你告诉编译器“我知道我在做什么”。

2
我认为问题在于JVM只支持两种类型的堆栈值:字长和双字长。因此,他们可能决定只需要一个操作来处理堆栈上的字长整数。因此,在字节码级别上只有iadd、imul等操作(没有字节和短整数的运算符)。
这样,您会得到一个int值作为这些操作的结果,Java不能安全地将其转换回更小的字节和短整数数据类型。所以他们强制您进行强制转换,以缩小值到byte/short。
但最终你是对的:这种行为与int的行为不一致,例如,您可以毫无问题地添加两个int并且如果结果溢出不会出错。

1
Java语言总是将算术运算符的参数提升为int、long、float或double类型。所以,考虑以下表达式:
a + b

其中a和b的类型为byte。这是一种简写形式:

(int)a + (int)b

这个表达式的类型是int。当将int值赋给byte变量时,显然会出现错误。

为什么语言要以这种方式定义呢?假设a是60,b是70,则a+b为-126 - 整数溢出。作为预期结果为int的更复杂表达式的一部分,这可能会成为一个难以解决的错误。限制使用byte和short只用于数组存储、文件格式/网络协议的常量和谜题。

JavaPolis 2007有一个有趣的录音。詹姆斯·高斯林(James Gosling)举了一个关于无符号算术有多么复杂(以及为什么它不在Java中)的例子。乔希·布洛赫(Josh Bloch)指出,在正常的有符号算术下,他的例子也给出了错误的示例。为了进行可理解的算术运算,我们需要任意精度。


0
在Java语言规范中(5.6.2二进制数值提升):
1. 如果任何表达式的类型为double,则提升类型为double,而不是double类型的其他表达式将进行扩展原始转换为double。
2. 否则,如果任何表达式的类型为float,则提升类型为float,而不是float类型的其他表达式将进行扩展原始转换为float。
3. 否则,如果任何表达式的类型为long,则提升类型为long,而不是long类型的其他表达式将进行扩展原始转换为long。
4. 否则,没有任何表达式的类型为double、float或long。在这种情况下,提升类型为int,并且任何不是int类型的表达式都将进行扩展原始转换为int。
你的代码属于第四种情况。变量a和b在传递给+运算符之前都被转换为int。+操作的结果也是int类型而不是byte类型。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接