为什么Java的+ =, – =,*=,/ =复合赋值运算符需要转换?

直到今天,我还以为:

i += j;

只是一个捷径:

i = i + j;

但是如果我们试试这个:

int i = 5;
long j = 8;

然后i = i + j;将不会编译但i += j;将编译正常.

这是否意味着事实上i += j;是这样的捷径
i = (type of i) (i + j)

回答

一如既往地提出这些问题,JLS就是答案.在本例中§15.26.2复合赋值运算符.提取物:

§15.26.2引用的一个例子

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

e>
并且结果x的值为7,因为它相当于:

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

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

  • @ronnieaka:我猜测语言设计师认为在一种情况下(`i + = j`),假设与其他情况相比需要精度损失更为安全(`i = i + j `)
  • 因此,当我检查自己时,`i + = j`编译,但这会导致精度损失吗?如果是这种情况,为什么不允许它在i = i + j中发生呢?为什么要在那里弄错
  • 不,它正好在我面前!对不起我之前没有注意到.在你的答案中,`E1 op = E2相当于E1 =(T)((E1)op(E2))`,所以这类似于隐式向下类型转换(从long到int).在i = i + j中,我们必须明确地做,即在`E1 =((E1)op(E2))中提供`(T)`部分`不是吗?
  • Java编译器为什么添加类型转换的一个可能原因是,如果您尝试对不兼容的类型执行算术运算,则无法使用缩减形式对结果进行类型转换.结果的类型转换通常比有问题的参数的类型转换更准确.当使用不兼容的类型时,没有类型转换会使收缩变得无用,因为它总是会导致编译器抛出错误.
  • 它没有四舍五入.它是演员(=截断)
  • 我假设在将短文本添加到双短语的情况下,这用于避免显式强制转换和/或`f`后缀?
  • @ RestlessC0bra:我真的非常希望JLS在这个级别上不会破坏:),所以不试试(你可以自己试试),我会说是的,"问题"仍然存在
  • 那么E1 op1 op2 op3 = E2是否有效且相当于E1 = T(E1 op3(E1 op2(E1 op1 E2)))?
  • @Hans:赋值运算符的左边不能有多个“操作” ...我敢肯定,这在JLS的其他地方都有

这个演员的一个很好的例子是使用*=或/ =

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

要么

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

要么

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

要么

char ch = 'A';
ch *= 1.5;
System.out.println(ch); // prints 'a'
  • @DavidWallace任何字符,只要它是'A`;)
  • 是的,但我可以看到一些初学者来到这里,阅读本文,并且认为你可以将任何字符从大写字母转换为小写字母,然后乘以1.5.
  • @PeterLawrey和@DavidWallace我会透露你的**秘密** - `ch + = 32` = D
  • @AkshatAgarwal ch是一个char.65*1.5 = 97.5 - >知道了?

非常好的问题.在Java语言规范确认您的建议.

并且结果x的值为7,因为它相当于:

  • 或者更有趣:"int x = 33333333; x + = 1.0f;".
  • @supercat,这是什么诡计?一个不正确舍入的扩展转换,然后是一个实际上不会改变结果的添加,再次转换为int以产生对于正常人类思维来说最意外的结果.

是,

基本上我们写的时候

i += l;

编译器将其转换为

i = (int)(i + l);

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

真的是一件好事

  • @glglgl我不同意人们应该依靠字体来区分这些情况......但是每个人都可以自由选择最好的东西.
  • @hexafraction:你的类文件是什么意思?如果你问我在帖子中提到的类文件比你的java类的编译版本
  • 你能告诉我这是哪个类文件吗?
  • 哦,你提到了"类"文件代码,这让我相信一个特定的类文件.我明白你的意思了.

你需要从浇铸longint explicitly在的情况下,i = i + l 那么它会编译并给出正确的输出.喜欢

i = i + (int)l;

要么

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

但是如果+=它只是工作正常,因为运算符隐式执行从右变量类型到左变量类型的类型转换,所以不需要显式转换.

  • 在这种情况下,"隐式演员"可能是有损的.实际上,正如@LukasEder在他的回答中所述,对'int`的强制转换是在*+'之后执行的.如果它确实将`long`强制转换为`int`,编译器会(应该?)抛出警告.

这里的问题涉及类型铸造.

当你添加int和long时,

  1. int对象被转换为long并且两者都被添加并且你得到长对象.
  2. 但是长对象不能隐式地转换为int.所以,你必须明确地这样做.

但是+=它的编码方式使它能够进行类型转换.i=(int)(i+m)


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

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

反之亦然.例如,我们不能自动将long转换为int,因为第一个需要比第二个更多的存储,因此信息可能会丢失.要强制执行此类转换,我们必须执行显式转换.
类型 - 转换

  • `float`不能保存每个可能的`int`值,而`double`不能保存每个可能的`long`值.
  • 嘿,但是`long`比`float`大2倍.
  • "安全转换"是什么意思?从答案的后半部分我可以推断出你的意思是自动转换(隐式转换),当然,浮动 - >长的情况下,这当然不是真的.float pi = 3.14f; 长b = pi; 将导致编译器错误.

有时候,可以在面试时询问这样的问题.

例如,当你写:

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

没有自动类型转换.在C++中,编译上面的代码不会有任何错误,但在Java中你会得到类似的东西Incompatible type exception.

所以要避免它,你必须编写如下代码:

int a = 2;
long b = 3;
a += b;// No compilation error or any exception due to the auto typecasting
  • 感谢您对C++中"op"使用与Java中使用的比较的深刻见解.我总是喜欢看到这些琐事,我确实认为他们为那些经常被遗漏的对话做出了贡献.
  • 但是问题本身*很有趣*,在面试*中问这个问题很愚蠢。这并不能证明该人可以产生高质量的代码,而只是证明他有足够的耐心来准备Oracle证书考试。通过使用危险的自动转换来“避免”不兼容的类型,从而隐藏可能的溢出错误,甚至可能*证明*该人无法产生可能质量好的代码。该死的所有这些自动转换和自动装箱的Java作者!

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

int a=5;
long b=10;
a+=b;
System.out.println(a);

你真正在做的是:

int a=5;
long b=10;
a=a+(int)b;
System.out.println(a);
  • 复合赋值运算符执行二进制运算结果的缩小转换,而不是右手操作数.所以在你的例子中,'a + = b'不等于'a = a +(int)b',但是,正如其他答案所解释的那样,'a =(int)(a + b)'.

这里微妙点......

对于i+jwhen j是double并且i是int,存在隐式类型转换.Java ALWAYS在它们之间有操作时将整数转换为double.

澄清i+=j哪里i是整数并且j是双精度可以描述为

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

请参阅:隐式转换的此描述

您可能要强制转换j(int)在这种情况下,为了清晰.


Java 语言规范定义E1 op= E2等效于E1 = (T) ((E1) op (E2))whereT是一种类型E1并且E1被评估一次。

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

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

这个程序打印什么?

你猜到3了吗?太糟糕了,这个程序不会编译。为什么?好吧,碰巧 Java 中的字节相加被定义为返回一个int. 我相信这是因为 Java 虚拟机没有定义字节操作来保存字节码(毕竟数量有限),而是使用整数操作是一种语言中公开的实现细节。

但是如果a = a + b不起作用,那将意味着a += b如果它E1 += E2被定义为E1 = E1 + E2. 正如前面的例子所示,情况确实如此。作为使+=操作符为字节和短裤工作的技巧,涉及到隐式转换。这不是什么了不起的黑客,但在 Java 1.0 工作期间,重点是让语言发布开始。现在,由于向后兼容,Java 1.0 中引入的这个 hack 无法删除。


以上是为什么Java的+ =, – =,*=,/ =复合赋值运算符需要转换?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>