ChatGPT解决这个技术问题 Extra ChatGPT

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

Until today, I thought that for example:

i += j;

Was just a shortcut for:

i = i + j;

But if we try this:

int i = 5;
long j = 8;

Then i = i + j; will not compile but i += j; will compile fine.

Does it mean that in fact i += j; is a shortcut for something like this i = (type of i) (i + j)?

I'm surprised Java allows this, being a stricter language than its predecessors. Errors in casting can lead to critical failure, as was the case with Ariane5 Flight 501 where a 64-bit float cast to a 16-bit integer resulted in the crash.
In a flight control system written in Java, this would be the least of your worries @SQLDiver
Actually i+=(long)j; even will compile fine.
The constant push by one set of developers for accuracy and another for ease of use is really interesting. We almost need two versions of the language, one that is amazingly precise and one that is easy to use. Pushing Java from both directions moves it towards being unsuitable for either group.
if it did require casting, where would you put it? i += (int) f; casts f before addition, so it's not equivalent. (int) i += f; casts the result after assignment, not equivalent either. there would be no place to put a cast that would signify that you want to cast the value after adding, but before assignment.

F
Flow

As always with these questions, the JLS holds the answer. In this case §15.26.2 Compound Assignment Operators. An extract:

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.

An example cited from §15.26.2

[...] the following code is correct: short x = 3; x += 4.6; and results in x having the value 7 because it is equivalent to: short x = 3; x = (short)(x + 4.6);

In other words, your assumption is correct.


So i+=j compiles as I checked myself, but it would result in loss of precision right? If that's the case, why doesn't it allow it to happen in i=i+j also? Why bug us there?
@ronnieaka: I'm guessing that the language designers felt that in one case (i += j), it is safer to assume that the loss of precision is desired as opposed to the other case (i = i + j)
P
Peter Lawrey

A good example of this casting is using *= or /=

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

or

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

or

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

or

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

@AkshatAgarwal ch is a char. 65 * 1.5 = 97.5 -> Got it?
Yeah, but I can just see some beginner coming here, reading this, and going away thinking that you can convert any character from upper case to lower case by multiplying it by 1.5.
@DavidWallace Any character as long as it is A ;)
@PeterLawrey & @DavidWallace I will reveal your secret- ch += 32 =D
K
Keiwan

Very good question. The Java Language specification confirms your suggestion.

For example, the following code is correct: short x = 3; x += 4.6; and results in x having the value 7 because it is equivalent to: short x = 3; x = (short)(x + 4.6);


Or more fun: "int x=33333333; x+=1.0f;".
@supercat , what trickery is this? A widening conversion that is incorrectly rounded, followed by an addition that doesn't actually change the result, casting to int again to produce a result that is most unexpected for normal human minds.
@neXus: IMHO, the conversion rules should have treated double->float as widening, on the basis that values of type float identify real numbers less specifically than those of type double. If one views double as a complete postal address and float as a 5-digit postal code, it's possible to satisfy a request for a postal code given a complete address, but it's not possible to accurately specify a request for a complete address given just a postal code. Converting a street address to a postal code is a lossy operation, but...
...someone who needs a complete address wouldn't generally be asking for just a postal code. Conversion from float->double is equivalent to converting US postal code 90210 with "US Post Office, Beverly Hills CA 90210".
p
psmears

Yes,

basically when we write

i += l; 

the compiler converts this to

i = (int)(i + l);

I just checked the .class file code.

Really a good thing to know


Can you tell me which classfile this is?
@hexafraction: what you mean by class file? if you asking about the class file i mentioned in my post than it's the complied version of your java class
Oh, you mentioned "the" class file code, which led me to believe a specific classfile was involved. I understand what you mean now.
@Bogdan That shouldn't be a problem with properly used fonts. A programmer which chooses whe wrong font for programming should clearly think about how to proceed...
@glglgl I disagree that one should rely on font to distinguish in those cases... but everybody has the freedom to choose what thinks is best.
d
dku.rajkumar

you need to cast from long to int explicitly in case of i = i + l then it will compile and give correct output. like

i = i + (int)l;

or

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

but in case of += it just works fine because the operator implicitly does the type casting from type of right variable to type of left variable so need not cast explicitly.


In this case, the "implicit cast" could be lossy. In reality, as @LukasEder states in his answer, the cast to int is performed after the +. The compiler would (should?) throw a warning if it really did cast the long to int.
s
slevy1

The problem here involves type casting.

When you add int and long,

The int object is casted to long & both are added and you get long object. but long object cannot be implicitly casted to int. So, you have to do that explicitly.

But += is coded in such a way that it does type casting. i=(int)(i+m)


t
tinker_fairy

In Java type conversions are performed automatically when the type of the expression on the right hand side of an assignment operation can be safely promoted to the type of the variable on the left hand side of the assignment. Thus we can safely assign:

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

The same will not work the other way round. For example we cannot automatically convert a long to an int because the first requires more storage than the second and consequently information may be lost. To force such a conversion we must carry out an explicit conversion.
Type - Conversion


Hey, but long is 2 times larger than float.
A float can't hold every possible int value, and a double can't hold every possible long value.
What do you mean by "safely converted" ? From latter part of the answer I can deduce that you meant automatic conversion ( implicit cast ) which is of course not true in case of float -> long. float pi = 3.14f; long b = pi; will result in compiler error.
You'd be better off differentiating floating point primitive types with integer primitive types. They're not the same thing.
Java has simplistic conversion rules that require the use of casts in many patterns where behavior without casts would otherwise match expectations, but doesn't require casts in many patterns that are usually erroneous. For example, a compiler will accept double d=33333333+1.0f; without complaint, even though the result 33333332.0 would likely not be what was intended (incidentally, the arithmetically-correct answer of 33333334.0f would be representable as either float or int).
T
Thomas

Sometimes, such a question can be asked at an interview.

For example, when you write:

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

there is no automatic typecasting. In C++ there will not be any error compiling the above code, but in Java you will get something like Incompatible type exception.

So to avoid it, you must write your code like this:

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

Thanks for the insight regarding the comparison of the op use in C++ to its use in Java. I always like seeing these bits of trivia and I do think they contribute something to the conversation that may often be left out.
However the question itself is interesting, asking this in an interview is stupid. It does not prove that the person can produce a good quality code - it just proves that he had enough patience to prepare for an Oracle certificate exam. And "avoiding" the incompatible types by using a dangerous auto-conversion and thus hiding the possible overflow error, probably even proves that the person is not able to produce probable a good quality code. Damn the Java authors for all these auto-conversions and auto-boxing and all!
R
Roy Shmuli

The main difference is that with a = a + b, there is no typecasting going on, and so the compiler gets angry at you for not typecasting. But with a += b, what it's really doing is typecasting b to a type compatible with a. So if you do

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

What you're really doing is:

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

Compound assignment operators perform a narrowing conversion of the result of the binary operation, not the right-hand operand. So in your example, 'a += b' is not equivalent to 'a = a + (int) b' but, as explained by other answers here, to 'a = (int)(a + b)'.
B
Biddut

Subtle point here...

There is an implicit typecast for i+j when j is a double and i is an int. Java ALWAYS converts an integer into a double when there is an operation between them.

To clarify i+=j where i is an integer and j is a double can be described as

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

See: this description of implicit casting

You might want to typecast j to (int) in this case for clarity.


I think a more interesting case might be int someInt = 16777217; float someFloat = 0.0f; someInt += someFloat;. Adding zero to someInt shouldn't affect its value, but promoting someInt to float may change its value.
K
Konrad Borowski

Java Language Specification defines E1 op= E2 to be equivalent to E1 = (T) ((E1) op (E2)) where T is a type of E1 and E1 is evaluated once.

That's a technical answer, but you may be wondering why that's a case. Well, let's consider the following program.

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

What does this program print?

Did you guess 3? Too bad, this program won't compile. Why? Well, it so happens that addition of bytes in Java is defined to return an int. This, I believe was because the Java Virtual Machine doesn't define byte operations to save on bytecodes (there is a limited number of those, after all), using integer operations instead is an implementation detail exposed in a language.

But if a = a + b doesn't work, that would mean a += b would never work for bytes if it E1 += E2 was defined to be E1 = E1 + E2. As the previous example shows, that would be indeed the case. As a hack to make += operator work for bytes and shorts, there is an implicit cast involved. It's not that great of a hack, but back during the Java 1.0 work, the focus was on getting the language released to begin with. Now, because of backwards compatibility, this hack introduced in Java 1.0 couldn't be removed.