为什么字节除法和短除法在 Java 中得到 int?

Why byte and short division results in int in Java?

在Java中,如果我们除以bytes、shorts或ints,我们总是得到int。如果其中一个操作数是 long,我们将得到 long.

我的问题是 - 为什么 byteshort 除法不会导致 byteshort?为什么总是 int?

显然我不是在寻找 "because JLS says so" 答案,我是在询问 Java 语言中此设计决策的技术原理。

考虑这个代码示例:

    byte byteA = 127;
    byte byteB = -128;
    short shortA = 32767;
    short shortB = -32768;
    int intA = 2147483647;
    int intB = - -2147483648;
    long longA = 9223372036854775807L;
    long longB = -9223372036854775808L;


    int byteAByteB = byteA/byteB;
    int byteAShortB = byteA/shortB;
    int byteAIntB = byteA/intB;
    long byteALongB = byteA/longB;

    int shortAByteB = shortA/byteB;
    int shortAShortB = shortA/shortB;
    int shortAIntB = shortA/intB;
    long shortALongB = shortA/longB;

    int intAByteB = intA/byteB;
    int intAShortB = intA/shortB;
    int intAIntB = intA/intB;
    long intALongB = intA/longB;

    long longAByteB = longA/byteB;
    long longAShortB = longA/shortB;
    long longAIntB = longA/intB;
    long longALongB = longA/longB;

byteA 除以 byteB 只能是一个字节,对吗?
那么为什么 byteAByteB 必须是 int?为什么 shortALongB 不能是 short
为什么intALongB一定是long,结果总是适合int,不是吗?

更新

正如@Eran 指出的那样,(byte)-128/(byte)-1 导致 128 不适合 byte。但为什么不 short 呢?

更新 2

接下来,正如@Eran(再次)指出的那样,(int) -2147483648 / (int) -1 也不适合 int 但结果仍然是 int,而不是 long

byteA divided by byteB can't be anything but a byte, can it?

它可以不是字节:

byteA = -128;
byteB = -1;
int div = byteA/byteB; // == 128, not a byte

猜测它是从 C 中采用的东西,可能是通过 C++。

在这些语言中,如果一个或多个参数是比 int 更窄的类型,则它们 总是 提升为 int。这发生在 之前 表达式被评估。它经常被忽视,因为结果操作被转换为它被分配到的类型,并且如果没有副作用,编译器可能会优化所有中间步骤。

在 Java 虽然它不是太有害。 (在 C 和 C++ 中,它会让你出局:两个大 unsigned short 的乘积会溢出 int,其行为是 undefined。)

请注意,如果其中一个参数大于 int,则表达式的类型是最大的参数类型。

我认为理由是简单的规则不会产生意外。结果总是两者中较宽的类型(最小为 int),它不依赖于操作。

更好的方法可能是始终扩大 +*-,除非明确(或可能隐含)缩小。也就是说,除非你使用强制转换,否则不要溢出或下溢。例如,/ 除非强制转换,否则始终可以是 doublelong 操作。

但是 C 并因此 Java 不这样做。

简而言之,它有一个简单的规则来处理这个问题,无论好坏。


看我的吐槽http://vanillajava.blogspot.co.uk/2015/02/inconsistent-operation-widen-rules-in.html

主要原因是机器通常只有对其原生整数类型(和浮点数)的添加指令。这就是为什么对于许多语言来说,算术表达式中使用最少的类型是 int(通常是在某种程度上对应于基本机器整数类型的类型)。

例如,i386 规范说:

ADD performs an integer addition of the two operands (DEST and SRC). The result of the addition is assigned to the first operand (DEST), and the flags are set accordingly. When an immediate byte is added to a word or doubleword operand, the immediate value is sign-extended to the size of the word or doubleword operand.

这意味着在内部任何字节值都被扩展为一个整数(或类似的)。毕竟这是合理的,因为处理器是 32/64 位,然后以这些大小执行任何算术。如果有可能以字节为单位进行算术运算,这通常不被认为是有用的。

JVM 规范说(作为补充)您有:iaddladdfadddadd。这只是反映了底层机器通常表现如此的事实。任何其他选择都是可能的,可能以性能下降为代价。

无论何时定义字节类型的变量,无论您在其对面键入什么,都应为字节类型。

也就是说只能是一个数字在一个字节范围内(-128到127)。

但是,当您输入 表达式 时,例如。 byteA/byteB 这与输入 文字 不同,例如。数字 127.

这是 Java 的性质 - 整数 是用于整数的默认数据类型。

默认情况下,当您对 表达式 进行赋值时,Java 会将其转换为默认数据类型(整数),尽管该表达式可能是一个字节的有效值。

那么发生的事情是,当您定义一个字节并将表达式分配为该字节的值时,Java 需要将其转换为整数:

int byteAByteB = byteA / byteB;

但是,您可以通过强制转换分配的表达式并因此 Java 将其视为一个字节来解决这个问题。

byte byteAByteB = (byte) (byteA / byteB);

这样您就可以告诉 Java 将其视为一个字节。 (也可以用short等来完成)