为什么Java的+=, -=, *=, /=运算符需要使用cast

Why don't Java's +=, -=, *=, /= compound assignment operators require casting?

直到今天,我都认为,例如:

1
i += j;

只是一条捷径:

1
i = i + j;

但如果我们尝试一下:

1
2
int i = 5;
long j = 8;

那么,i = i + j;将不编译,但i += j;将编译罚款。

这是否意味着,事实上,i += j;是实现这一目标的捷径?EDOCX1?3?


像往常一样,对于这些问题,JLS持有答案。在这种情况下,§15.26.2复合分配运算符。摘录:

A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T)((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.

引用自§15.26.2的示例

[...] the following code is correct:

1
2
short x = 3;
x += 4.6;

and results in x having the value 7 because it is equivalent to:

1
2
short x = 3;
x = (short)(x + 4.6);

换句话说,你的假设是正确的。


这种铸造的一个好例子是使用*=或/=

1
2
3
byte b = 10;
b *= 5.7;
System.out.println(b); // prints 57

1
2
3
byte b = 100;
b /= 2.5;
System.out.println(b); // prints 40

1
2
3
char ch = '0';
ch *= 1.1;
System.out.println(ch); // prints '4'

1
2
3
char ch = 'A';
ch *= 1.5;
System.out.println(ch); // prints 'a'


很好的问题。Java语言规范证实了您的建议。

For example, the following code is correct:

1
2
short x = 3;
x += 4.6;

and results in x having the value 7 because it is equivalent to:

1
2
short x = 3;
x = (short)(x + 4.6);


对,

基本上当我们写作的时候

1
i += l;

编译器将此转换为

1
i = (int)(i + l);

我刚刚检查了.class文件代码。

真是个好消息


你需要从longintexplicitly的转换,如果是i = i + l的话,它会编译并给出正确的输出。喜欢

1
i = i + (int)l;

1
i = (int)((long)i + l); // this is what happens in case of += , dont need (long) casting since upper casting is done implicitly.

但在+=的情况下,它工作得很好,因为运算符隐式地将类型从右变量类型转换为左变量类型,因此不需要显式转换。


这里的问题涉及类型转换。

当你加上int和long时,

  • int对象被强制转换为long&both都被添加并且得到long对象。
  • 但long对象不能隐式强制转换为int,因此,必须显式执行。
  • 但是,+=的编码方式使得它可以进行类型转换。i=(int)(i+m)


    在Java类型转换中,当赋值操作右侧的表达式的类型可以被安全地提升为赋值左侧的变量类型时,自动执行转换。因此,我们可以安全地分配:

    1
     byte -> short -> int -> long -> float -> double.

    反过来,这也不会奏效。例如,我们不能自动将long转换为int,因为第一个函数比第二个函数需要更多的存储空间,因此信息可能会丢失。要强制进行这种转换,我们必须进行显式转换。类型-转换


    有时,这样的问题可以在面试时问。

    例如,当你写:

    1
    2
    3
    int a = 2;
    long b = 3;
    a = a + b;

    没有自动排版。在C++中,编译上述代码不会有任何错误,但是在爪哇中,你会得到一些类似EDCOX1×8的东西。

    为了避免这种情况,您必须这样编写代码:

    1
    2
    3
    int a = 2;
    long b = 3;
    a += b;// No compilation error or any exception due to the auto typecasting


    主要的区别在于,对于a = a + b,没有进行任何类型转换,因此编译器会因为没有进行类型转换而生您的气。但是对于a += b,它真正要做的是将b打印成与a兼容的类型。所以如果你这样做

    1
    2
    3
    4
    int a=5;
    long b=10;
    a+=b;
    System.out.println(a);

    你真正要做的是:

    1
    2
    3
    4
    int a=5;
    long b=10;
    a=a+(int)b;
    System.out.println(a);


    这里有点微妙…

    j是双精度的,i是int时,i+j有一个隐式类型转换。当它们之间有一个操作时,Java总是将一个整数转换成一个整数。

    为了澄清i+=j中,i是整数,j是双精度数,可以将其描述为

    1
    i = <int>(<double>i + j)

    参见:隐式强制转换的描述

    为了清晰起见,您可能需要将j转换为(int)


    Java语言规范定义EDCOX1〔0〕相当于EDCOX1〔1〕,其中EDCOX1〔2〕是EDCX1〔3〕的一种类型,EDCOX1〔3〕被评价一次。

    这是一个技术性的答案,但你可能想知道为什么会这样。好吧,让我们考虑下面的程序。

    1
    2
    3
    4
    5
    6
    7
    8
    public class PlusEquals {
        public static void main(String[] args) {
            byte a = 1;
            byte b = 2;
            a = a + b;
            System.out.println(a);
        }
    }

    这个程序打印什么?

    你猜到3了吗?太糟糕了,这个程序无法编译。为什么?那么,在Java中添加字节是为了返回EDCOX1×5的定义。我相信这是因为Java虚拟机没有定义字节操作来保存字节码(毕竟,这些字节数有限),相反,使用整数操作是用语言公开的实现细节。

    但如果a = a + b不起作用,这意味着如果E1 += E2被定义为E1 = E1 + E2的话,a += b就永远不会对字节起作用。如前一个例子所示,情况确实如此。作为使+=运算符适用于字节和短路的黑客,涉及到隐式强制转换。这不是一个伟大的黑客,但在Java 1的工作回来,重点放在语言的发布开始。现在,由于向后兼容,在Java 1中引入的这个黑客无法被删除。