Why Don't Java'S +=, -=, *=, /= Compound Assignment Operators Require Casting

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

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.

Why does this assignment not require an explicit cast?

Well to start with, b+l gives a long, not an int...

... but compound assignment operators have other behaviour. As per JLS section 15.26.2:

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.

Note the cast to T.

Are compound assignment operators less precise than in c++?

In C++ if both operands i, j are of the same type then no conversion is done.

If they are different then the lower range (e.g. int) is promoted to higher range (e.g. long or float or double)

This promotion, from down to up, most times doesn't lose precision. But some int-to-float conversions may lose some digits.

There's no difference between i+=j and i=i+j.

In some cases you must be aware of loosing precision. For example:

int i = 2;
double d= 4.8;
i += d;

Because d is higher-range than i the operation is made with doubles: 2+4.8 = 6.8
But at the time of storing it back to i the result is truncated, so i=6

For the case int i, long j the operations (i+j) are done at long precision, but some lose may happen at i if j doesn't fits into an int.

Why doesn't compound assignment in Java catch overflow problems?

Here is one explanation:

When you do an assignment (the first code snippet), java enforces type checking because the LHS and RHS may very well be independent of each other.

But the compound-operator is more like
an incremental operator. The +=
modifies the value of the variable
involved, rather than assigning a new
value to the variable. When you modify
a byte, you expect a byte as the
result. To make life easier, java does
an implicit type conversion for
compound-operators because they are
modifiers.

Why does compound assignment (+=) differ between languages (Java, C++)?

This has more to do with evaluation order than "what the compound assignment operators do", so you'll find more useful things in the "evaluation order" sections of the specs of both languages.

For Java, JLS §15.7:

The left-hand operand of a binary operator appears to be fully
evaluated before any part of the right-hand operand is evaluated.

If the operator is a compound-assignment operator (§15.26.2), then
evaluation of the left-hand operand includes both remembering the
variable that the left-hand operand denotes and fetching and saving
that variable's value for use in the implied binary operation.

So n on the left of += evaluates first, to 0. Then the right hand side evaluates to 3. The sum of this value and the left hand side is then written to n.

For C++, Evaluation Order:

Look at item 20 in the "Rules" section:

In every simple assignment expression E1=E2 and every compound assignment expression E1@=E2, every value computation and side-effect of E2 is sequenced before every value computation and side effect of E1

Here, E2 (the right hand side) is evaluated first, to 3, then the left hand side is evaluated. At that point, n is already changed to 3 by f, so the left hand side evaluates to 3 as well.

Confusion about compound assingnments (+=, -=, *=, ...) in Java

JLS 15.26.2 says following, https://docs.oracle.com/javase/specs/jls/se9/html/jls-15.html#jls-15.26.2

If the left-hand operand expression is not an array access expression, then:

First, the left-hand operand is evaluated to produce a variable. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the right-hand operand is not evaluated and no assignment occurs.

Otherwise, the value of the left-hand operand is saved and then the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.

Otherwise, the saved value of the left-hand variable and the value of the right-hand operand are used to perform the binary operation indicated by the compound assignment operator. If this operation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.

Otherwise, the result of the binary operation is converted to the type of the left-hand variable, subjected to value set conversion (§5.1.13) to the appropriate standard value set (not an extended-exponent value set), and the result of the conversion is stored into the variable.

So the original value of x, i.e. 1, is saved, then the RHS is evaluated. Therefore it's -3.

Why does the augmented assignement operator in Java give a different result?

From the JLS compound assigment operator documentation, you can see the following example :

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

It simply auto cast the result by default.

PS : In this other answer I tried to explain the reason you need to cast an operation like the following using the JLS

short x = 3;
x = x + 1; //Won't compile

It is realativly new so I am open to suggestion there.

Why does *= not give any errors when implicitly casting a float to an int?

Compound assignment operators behave a little differently than their "expanded" version. Quoting the JLS, Section 15.26.2:

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.

It is implicitly casted back to the type of the variable on the left side, so that there is no error casting a float to an int; it's already implicitly casted to an int.

That does not occur with the = operator, which is governed by the JLS, Section 5.2, Assignment Conversion:

Assignment contexts allow the use of one of the following:

  • an identity conversion (§5.1.1)

  • a widening primitive conversion (§5.1.2)

  • a widening reference conversion (§5.1.5)

  • a boxing conversion (§5.1.7) optionally followed by a widening reference conversion

  • an unboxing conversion (§5.1.8) optionally followed by a widening primitive conversion.

It goes on to talk about a possible narrowing conversion being allowed, but only for constant expressions, and only for the constant expression being a byte, char, short, or int, neither of which is applicable here.



Related Topics



Leave a reply



Submit