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

12

3855

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

Natant answered 3/1, 2012 at 10:10 Comment(17)
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.Mccahill
In a flight control system written in Java, this would be the least of your worries @MccahillPasqualepasqueflower
Actually i+=(long)j; even will compile fine.Rior
I think Douglas Crockford wins the argument after all... This behavior is terrible and lots of languages have similar problems around conversions.Patmore
@Tharindu that's consistent though, since the whole expression gets casted, so it's the same as as i = (int)(i + (long)j);Trophoblast
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.Mucous
@RossDrew Why is that not a good idea?Fontana
@Tharindu What did you excpect? You are casting j to long, but j is already a long, so your casting doesn't do anything.Fontana
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.Bucentaur
"it's floating point is not precise" let's correct that-> "floating point is not precise" (Java follows IEEE floating point standard). If you use floating point when calculations and you haven't analysed whether the error is within the bounds required for your application, and your application is critical, you are the one to blame - not the language.Gross
@RossDrew The Ariane5 was a space rocket.Mccahill
This is a huge source of bugs in Java.Haggi
@BillK no, we do not need two versions. We only need the one version for accuracy. The "ease of use" is an illusion. If you opt for features that provide "ease of use" you invariably end up paying for them hundredfold in maintenance costs. Let me point you to good old edsgar: cs.utexas.edu/users/EWD/transcriptions/EWD06xx/EWD667.html (search for "ease of programming" in the text.)Abbotson
@MikeNakis The lack of paying attention to ease of use has lead to people not being interested in or even hating java--especially new users. This has lead to quite a few easy to use & inaccurate languages along the line of ruby/groovy. Kotlin seems to be a good balance of a usable static language, but it's yet another response to Java not moving towards usability. (Don't get me wrong, I LOVE Java and they have made inroads towards usability such as not requiring two wrapper classes to read a text file, it just may be too late).Mucous
As already mentionned, there is no good place to put a cast so the only other option would be to make it error and then you would have to use the long expression i = (int)(i + j). Probably the best option would be to have a compiler option to generate a warning or error...Werby
@LukeHutchison Is it? Evidence?. I’ve been programming in Java since 1997 and I’ve never seen such a bug.Dossier
@Dossier yes, this is quite obviously a source of loss-of-precision bugs, since it allows implicit narrowing casts without any sort of warning to the user. There's a reason you can't do i = j; using the example in the question. If that were allowed without warning, you'd be throwing away half of j without even knowing it. The problem is compounded because implicit widening casts are allowed. So for example, consider: double x = 0.0; int y = 1; double z = 1.5; y += z; x += y; : there are no warnings anywhere, but if you don't realize y is an int, you lose .5 with no warning.Haggi
D
2563

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.

Daley answered 3/1, 2012 at 10:15 Comment(2)
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?Fugato
@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)Daley
P
508

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'
Priesthood answered 3/1, 2012 at 10:20 Comment(4)
@AkshatAgarwal ch is a char. 65 * 1.5 = 97.5 -> Got it?Santasantacruz
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.Morehouse
@DavidWallace Any character as long as it is A ;)Priesthood
@PeterLawrey & @DavidWallace I will reveal your secret- ch += 32 =DManouch
U
270

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);
Ulland answered 3/1, 2012 at 10:17 Comment(4)
Or more fun: "int x=33333333; x+=1.0f;".Unwarrantable
@Unwarrantable , 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.Unlimited
@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...Unwarrantable
...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".Unwarrantable
T
183

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.

Tris answered 3/1, 2012 at 10:19 Comment(6)
Can you tell me which classfile this is?Tannenwald
@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 classTris
Oh, you mentioned "the" class file code, which led me to believe a specific classfile was involved. I understand what you mean now.Tannenwald
@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...Kallman
@Kallman I disagree that one should rely on font to distinguish in those cases... but everybody has the freedom to choose what thinks is best.Enrichetta
On some old MATLAB version, the default font size was one which made O and 0 look the same in Times New Roman, as well as 1 and l. It was always the first step that I did to change the font sie to something useful...Kallman
P
94

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.

Photochronograph answered 3/1, 2012 at 10:15 Comment(1)
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.Spitball
I
64

The problem here involves type casting.

When you add int and long,

  1. The int object is cast to long & both are added and you get a long object.
  2. But the long object cannot be implicitly cast 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)

Ingmar answered 3/1, 2012 at 10:20 Comment(0)
T
53

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

Thibodeaux answered 23/1, 2013 at 5:50 Comment(5)
Hey, but long is 2 times larger than float.Buffybuford
A float can't hold every possible int value, and a double can't hold every possible long value.Grommet
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.Mistrust
You'd be better off differentiating floating point primitive types with integer primitive types. They're not the same thing.Joellenjoelly
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).Unwarrantable
A
46

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
Authors answered 2/12, 2014 at 10:40 Comment(2)
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.Suzette
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!Psychodiagnostics
E
27

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);
Echikson answered 7/6, 2015 at 23:27 Comment(1)
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)'.Jimerson
S
12

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.

Shala answered 18/1, 2016 at 20:7 Comment(1)
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.Unwarrantable
P
9

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.

Phytobiology answered 8/3, 2019 at 16:6 Comment(0)
F
0

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.

Frame answered 2/6, 2023 at 4:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.