3728

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

GMachado
  • 681
  • 10
  • 21
Honza Brabec
  • 36,288
  • 3
  • 19
  • 30
  • 142
    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. – SQLDiver Jan 05 '15 at 16:31
  • 117
    In a flight control system written in Java, this would be the least of your worries @SQLDiver – Ross Drew Nov 02 '15 at 09:24
  • 10
    Actually `i+=(long)j;` even will compile fine. – Tharindu Sathischandra Mar 18 '16 at 07:45
  • I think Douglas Crockford wins the argument after all... This behavior is terrible and lots of languages have similar problems around conversions. – Aluan Haddad Jul 20 '17 at 13:44
  • 1
    @Tharindu that's consistent though, since the whole expression gets casted, so it's the same as as `i = (int)(i + (long)j);` – Ryan Haining Aug 21 '17 at 00:43
  • 6
    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. – Bill K Sep 19 '17 at 16:03
  • 2
    @RossDrew Why is that not a good idea? – HelloGoodbye Oct 18 '18 at 09:52
  • 2
    @Tharindu What did you excpect? You are casting `j` to `long`, but `j` is already a `long`, so your casting doesn't do anything. – HelloGoodbye Oct 18 '18 at 09:54
  • 7
    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. – Norill Tempest Oct 24 '18 at 13:09
  • "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. – Erwin Bolwidt Jan 22 '19 at 23:07
  • @RossDrew The Ariane5 was a space rocket. – SQLDiver Sep 06 '19 at 13:48
  • This is a huge source of bugs in Java. – Luke Hutchison Dec 03 '20 at 00:25

11 Answers11

2502

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.

Flow
  • 22,048
  • 13
  • 91
  • 147
Lukas Eder
  • 181,694
  • 112
  • 597
  • 1,319
  • 43
    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? – bad_keypoints Sep 22 '12 at 06:07
  • 47
    @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`) – Lukas Eder Sep 22 '12 at 08:31
  • 12
    No, its right there, infront of me! Sorry i didn't notice it earlier. As in your answer, `E1 op= E2 is equivalent to E1 = (T)((E1) op (E2))`, so that's kind of like implicit down typecasting (down from long to int). Whereas in i = i+j, we have to do it explicitly, ie, provide the `(T)` part in `E1 = ((E1) op (E2))` Isn't it? – bad_keypoints Sep 22 '12 at 14:52
  • 4
    I'd assume that this is used to avoid an explicit cast and/or `f` postfix in the situation of adding a double literal to a short? – nanofarad Aug 12 '13 at 14:28
  • 2
    So is E1 op1 op2 op3 = E2 valid and equivalent to E1 = T(E1 op3 (E1 op2 (E1 op1 E2)))? – Hans Feb 25 '14 at 00:28
  • 2
    @Hans: You can't have several "ops" to the left of an assignment operator... I'm sure this is covered elsewhere in the JLS – Lukas Eder Feb 25 '14 at 06:53
  • 11
    A likely reason to why the Java compiler adds a typecast is because if you're trying to perform arithmetic on incompatible types, there is no way of performing a typecast of the result using the contracted form. A typecast of the result is generally more accurate than a typecast of the problematic argument. No typecast would make the contraction useless when using incompatible types, as it would always cause the compiler to throw an error out. – ThePyroEagle Dec 22 '15 at 21:04
  • Why is 3 + 4.6 rounded to 7, not 8? – pyb Apr 02 '16 at 21:14
  • 6
    It's not rounded. It's cast (= truncated) – Lukas Eder Apr 03 '16 at 12:40
  • 1
    The _Puzzle 9_ in the book "_Java Puzzlers_" (Joshua Bloch, Neal Gafter) covers exactly this issue. In the solution the authors highlight 2 phrases: "_Compound assignment expressions automatically cast the result of the computation they perform to the type of the variable on their left-hand side._" --- "_Do not use compound assignment operators on variables of type ```byte```, ```short```, or ```char```._" – Stefan Neuhaus Oct 15 '16 at 17:11
  • 5
    @RestlessC0bra: I really really really hope that the JLS won't break incompatibly on that level :), so without trying (you could try this yourself), I'll say yes, the "issue" persists – Lukas Eder Jul 14 '17 at 10:20
495

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'
Peter Lawrey
  • 498,481
  • 72
  • 700
  • 1,075
262

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);
Keiwan
  • 7,331
  • 5
  • 31
  • 48
Thirler
  • 18,868
  • 13
  • 58
  • 86
  • 17
    Or more fun: "int x=33333333; x+=1.0f;". – supercat Apr 20 '17 at 19:31
  • 5
    @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 Sep 21 '18 at 12:33
  • 1
    @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*... – supercat Sep 21 '18 at 15:05
  • 1
    ...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". – supercat Sep 21 '18 at 15:07
182

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

psmears
  • 21,582
  • 4
  • 37
  • 47
Umesh Awasthi
  • 22,642
  • 37
  • 122
  • 198
  • 3
    Can you tell me which classfile this is? – nanofarad Aug 12 '13 at 14:29
  • 7
    @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 – Umesh Awasthi Aug 12 '13 at 15:17
  • 4
    Oh, you mentioned "the" class file code, which led me to believe a specific classfile was involved. I understand what you mean now. – nanofarad Aug 12 '13 at 18:57
  • 1
    @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 Feb 16 '15 at 06:08
  • 7
    @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. – Bogdan Alexandru Feb 16 '15 at 14:42
  • 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... – glglgl Feb 16 '15 at 20:34
92

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.

dku.rajkumar
  • 17,470
  • 7
  • 39
  • 57
  • 7
    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`. – Romain Jan 03 '12 at 10:17
64

The problem here involves type casting.

When you add int and long,

  1. The int object is casted to long & both are added and you get long object.
  2. 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)

slevy1
  • 3,623
  • 2
  • 23
  • 30
dinesh028
  • 2,057
  • 4
  • 28
  • 43
55

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

tinker_fairy
  • 1,255
  • 1
  • 13
  • 18
  • 2
    Hey, but `long` is 2 times larger than `float`. – Display Name Aug 30 '13 at 10:17
  • 11
    A `float` can't hold every possible `int` value, and a `double` can't hold every possible `long` value. – Alex MDC Sep 09 '13 at 20:41
  • 2
    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. – Luke Nov 07 '13 at 13:21
  • 1
    You'd be better off differentiating floating point primitive types with integer primitive types. They're not the same thing. – ThePyroEagle Dec 22 '15 at 20:59
  • 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`). – supercat Apr 20 '17 at 19:40
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
Thomas
  • 4,827
  • 5
  • 28
  • 60
Stopfan
  • 1,441
  • 13
  • 21
  • 6
    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. – Thomas Jan 14 '15 at 20:50
  • 2
    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! – Honza Zidek Mar 26 '18 at 11:08
25

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);
Roy Shmuli
  • 4,489
  • 1
  • 21
  • 38
takra
  • 445
  • 5
  • 14
  • 5
    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)'. – Lew Bloch Jan 09 '16 at 21:14
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.

Biddut
  • 319
  • 3
  • 14
Gabe Nones
  • 193
  • 2
  • 11
  • 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. – supercat Aug 13 '18 at 20:27
6

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.

Konrad Borowski
  • 9,885
  • 2
  • 50
  • 68