为什么 Java 的 +=、-=、*=、/= 复合赋值运算符不需要将 long 转换为 int?

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

提问人:Honza Brabec 提问时间:1/3/2012 最后编辑:cellepoHonza Brabec 更新时间:7/5/2023 访问量:316417

问:

直到今天,我还以为:

i += j;

只是以下方面的捷径:

i = i + j;

但是,如果我们尝试这样做:

int i = 5;
long j = 8;

然后不会编译,但会编译得很好。i = i + j;i += j;

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

Java 强制转换 赋值 运算符

评论

158赞 SQLDiver 1/6/2015
我很惊讶 Java 允许这样做,因为它是一种比它的前辈更严格的语言。转换错误可能导致严重故障,就像 Ariane5 Flight 501 的情况一样,其中 64 位浮点转换为 16 位整数导致崩溃。
137赞 Ross Drew 11/2/2015
在用 Java 编写的飞行控制系统中,这将是您最不担心的@SQLDiver
10赞 Tharindu Sathischandra 3/18/2016
实际上甚至会编译得很好。i+=(long)j;
8赞 Bill K 9/20/2017
一组开发人员不断追求准确性,另一组开发人员不断追求易用性,这真的很有趣。我们几乎需要两个版本的语言,一个是惊人的精确版本,另一个是易于使用的版本。从两个方向推动 Java 会使其不适合任何一组。
9赞 Norill Tempest 10/24/2018
如果它确实需要铸造,你会把它放在哪里? 在加法之前强制转换 F,因此它不等价。 在赋值后强制转换结果,也不等效。没有地方可以放置一个强制转换,该强制转换表示您要在添加之后但在赋值之前强制转换值。i += (int) f;(int) i += f;

答:

2561赞 Lukas Eder 1/3/2012 #1

与这些问题一样,JLS拥有答案。在本例中为 §15.26.2 复合赋值运算符。摘录:

形式的复合赋值表达式等价于 ,其中 是 的类型,只不过只计算一次。E1 op= E2E1 = (T)((E1) op (E2))TE1E1

引自 §15.26.2 的示例

[...]以下代码正确:

short x = 3;
x += 4.6;

并导致 x 的值为 7,因为它等价于:

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

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

评论

43赞 bad_keypoints 9/22/2012
所以在我检查自己时编译,但这会导致精度下降,对吧?如果是这样的话,为什么它不允许它在 i=i+j 中也发生呢?为什么要在那里打扰我们?i+=j
49赞 Lukas Eder 9/22/2012
@ronnieaka:我猜语言设计者认为,在一种情况下(),假设精度损失是需要的,而不是另一种情况(i += ji = i + j)
94赞 dku.rajkumar 1/3/2012 #2

您需要从 to 强制转换,以防万一,它将编译并给出正确的输出。喜欢longintexplicitlyi = 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.

但是,在这种情况下,它工作正常,因为运算符隐式地执行从右变量类型到左变量类型的类型转换,因此不需要显式转换。+=

评论

7赞 Romain 1/3/2012
在这种情况下,“隐式强制转换”可能是有损的。实际上,正如@LukasEder在他的回答中所说,演员表是在 .如果编译器确实将 .int+longint
270赞 Thirler 1/3/2012 #3

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

例如,以下代码是正确的:

short x = 3;
x += 4.6;

并导致 x 的值为 7,因为它等价于:

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

评论

22赞 supercat 4/21/2017
或者更有趣:“int x=33333333;x+=1.0f;“.
5赞 neXus 9/21/2018
@supercat,这是什么诡计?一个错误地四舍五入的扩大转换,然后是实际上并没有改变结果的加法,再次转换为 int 以产生正常人心中最意想不到的结果。
2赞 supercat 9/21/2018
@neXus:恕我直言,转换规则应该被视为扩大,因为 type 的值比 type 的值更具体地标识实数。如果将邮件视为完整的邮政地址和 5 位数的邮政编码,则可以满足给定完整地址的邮政编码请求,但不可能仅给定邮政编码即可准确指定完整地址的请求。将街道地址转换为邮政编码是一项有损操作,但是......double->floatfloatdoubledoublefloat
2赞 supercat 9/21/2018
...需要完整地址的人通常不会只要求提供邮政编码。转换自相当于将美国邮政编码 90210 转换为“US Post Office, Beverly Hills CA 90210”。float->double
183赞 Umesh Awasthi 1/3/2012 #4

是的

基本上当我们写

i += l; 

编译器将其转换为

i = (int) (i + l);

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

知道真的是一件好事。

评论

3赞 nanofarad 8/12/2013
你能告诉我这是哪个类文件吗?
8赞 Umesh Awasthi 8/12/2013
@hexafraction:类文件是什么意思?如果你问我在帖子中提到的类文件,那么它是你的 Java 类的编译版本
4赞 nanofarad 8/13/2013
哦,你提到了“那个”类文件代码,这让我相信涉及一个特定的类文件。我现在明白你的意思了。
1赞 glglgl 2/16/2015
@Bogdan 正确使用字体应该不是问题。如果程序员选择了错误的字体进行编程,那么应该清楚地考虑如何进行......
7赞 Bogdan Alexandru 2/16/2015
@glglgl我不同意在这些情况下应该依靠字体来区分......但每个人都有选择认为最好的自由。
508赞 Peter Lawrey 1/3/2012 #5

这种强制转换的一个很好的例子是使用 *= 或 /=

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'

评论

12赞 Sajal Dutta 5/6/2014
@AkshatAgarwal ch 是一个字符。 65 * 1.5 = 97.5 -> 明白了吗?
91赞 Dawood ibn Kareem 5/28/2014
是的,但我可以看到一些初学者来到这里,读到这篇文章,然后离开,认为你可以通过将任何字符乘以 1.5 将任何字符从大写转换为小写。
110赞 Peter Lawrey 5/28/2014
@DavidWallace 任何字符,只要是A ;)
18赞 Minhas Kamal 3/16/2016
@PeterLawrey & @DavidWallace 我会透露你的秘密 - =Dch += 32
64赞 dinesh028 1/3/2012 #6

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

当您添加 int 和 long 时,

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

但是以这样一种方式进行编码,即它进行类型转换。+=i = (int) (i + m)

53赞 tinker_fairy 1/23/2013 #7

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

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

反之亦然。例如,我们不能自动将 long 转换为 int,因为第一个比第二个需要更多的存储空间,因此信息可能会丢失。为了强制这样的转换,我们必须进行明确的转换。
类型 - 转换

评论

2赞 Display Name 8/30/2013
嘿,但比 .longfloat
11赞 Alex MDC 9/10/2013
A 不能保存所有可能的值,而 A 不能保存所有可能的值。floatintdoublelong
2赞 Luke 11/7/2013
“安全皈依”是什么意思?从答案的后半部分,我可以推断出您的意思是自动转换(隐式转换),这在浮点数 -> long 的情况下当然不是真的。浮点 pi = 3.14f;长 b = 圆周率;将导致编译器错误。
1赞 ThePyroEagle 12/23/2015
最好将浮点基元类型与整数基元类型区分开来。它们不是一回事。
0赞 supercat 4/21/2017
Java 具有简单的转换规则,这些规则要求在许多模式中使用强制转换,而没有强制转换的行为在其他情况下会符合预期,但不需要在许多通常错误的模式中使用强制转换。例如,编译器将毫无怨言地接受,即使结果 33333332.0 可能不是预期的(顺便说一句,333333334.0f 的算术正确答案可以表示为 either 或 )。double d=33333333+1.0f;floatint
46赞 Stopfan 12/2/2014 #8

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

例如,当您编写:

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

评论

6赞 Thomas 1/15/2015
感谢您对 C++ 中的使用与 Java 中的使用进行比较的见解。我总是喜欢看到这些琐事,我确实认为它们有助于对话,而这些对话可能经常被遗漏。op
2赞 Honza Zidek 3/26/2018
然而,这个问题本身很有趣在采访中问这个问题是愚蠢的。这并不能证明这个人可以生成高质量的代码 - 它只是证明他有足够的耐心来准备 Oracle 证书考试。通过使用危险的自动转换来“避免”不兼容的类型,从而隐藏可能的溢出错误,甚至可能证明该人无法生成可能高质量的代码。该死的 Java 作者所有这些自动转换和自动装箱等等!
27赞 takra 6/8/2015 #9

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

    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);

评论

6赞 Lew Bloch 1/10/2016
复合赋值运算符对二进制运算的结果(而不是右手操作数)执行缩小转换。因此,在您的示例中,“a += b”不等同于“a = a + (int) b”,而是像此处的其他答案所解释的那样,等同于“a = (int)(a + b)”。
12赞 Cyborg 1/19/2016 #10

微妙的一点在这里......

when 是 double 并且是 int 有一个隐式类型转换。 Java 总是在整数之间进行运算时将整数转换为双精度值。i+jji

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

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

请参阅:隐式强制转换的此说明

为清楚起见,在这种情况下,您可能希望键入 to。j(int)

评论

1赞 supercat 8/14/2018
我认为一个更有趣的案例可能是.将零添加到不应影响其值,但提升到可能会更改其值。int someInt = 16777217; float someFloat = 0.0f; someInt += someFloat;someIntsomeIntfloat
9赞 Kamila Borowska 3/9/2019 #11

Java 语言规范将 E1 op= E2 定义为等价于 E1 = (T) ((E1) op (E2)),其中 T 是 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 虚拟机没有定义字节操作来节省字节码(毕竟字节码的数量有限),而是使用整数操作是语言中公开的实现细节。

但是,如果不起作用,则意味着如果将其定义为 ,则永远不会对字节起作用。正如前面的例子所表明的,情况确实如此。作为使 operator 为 bytes 和 short 工作的 hack,涉及隐式转换。这并不是一个伟大的黑客,但在 Java 1.0 工作期间,重点是让语言从一开始就发布。现在,由于向后兼容性,Java 1.0 中引入的这个 hack 无法删除。a = a + ba += bE1 += E2E1 = E1 + E2+=

0赞 Lalita Garg 6/2/2023 #12

与这些问题一样,JLS拥有答案。在本例中为 §15.26.2 复合赋值运算符。摘录:

E1 op= E2 形式的复合赋值表达式等价于 E1 = (T)((E1) op (E2)),其中 T 是 E1 的类型,只是 E1 只计算一次。

引自 §15.26.2 的示例

[...]以下代码正确:

short x = 3;
x += 4.6;

并导致 x 的值为 7,因为它等价于:

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

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