Primitive type casting - java

I have been working with Java's byte primitive lately, and I came across a silly problem:
byte a = 10;
byte b = 9;
byte c = 8;
b += b*c; // how come this statement is correct without any explicit type casting
b = b*c; // while this statement is incorrect; it requires explicit cast, of course
b = b+(b*c); // this is wrong too.
So my question is, does += specify any assignment other than just add and assign, or is this a bug in Java (which I am almost sure is not)?

Because b += b*c is equivalent to b += (byte) ((b) + (b*c)).
From the Java language specification on compound assignment operators:
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.

All compound assignment operators not only performs the operation, but also cast automatically their result to the type of the left-hand side variable.
So your += not only adds variables and assign the result - it also cast the result to the right type.

Related

Java cast confusing [duplicate]

I've found that java compile has a non-expected behavior regarding assignment and self assignment statements using an int and a float.
The following code block illustrates the error.
int i = 3;
float f = 0.1f;
i += f; // no compile error, but i = 3
i = i + f; // COMPILE ERROR
In the self assignment i += f the compile does not issue an error, but the result of the exaluation is an int with value 3, and the variable i maintains the value 3.
In the i = i + f expression the compiler issues an error with "error: possible loss of precision" message.
Can someone explain this behavior.
EDIT: I've posted this code block in https://compilr.com/cguedes/java-autoassignment-error/Program.java
http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.26.2
The Java Language Specification says:
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.
So i += f is equivalent to i = (int) (i + f).
i believe that the explicit i+f fails because of Narrowing primitive conversion. While in the first case the conversion on the right side passes because it is done according to Compound Assignment rules.

Byte unary operation

We all know that in Java these operators:
a++;
++a;
a += 1;
a = a + 1;
do the same thing, they simply add 1 to variable 'a'
However why these statements are not all true, what is the principle behind this?
byte a = 1;
a++;
++a;
a += 1;
a = a + 1; // This line will result to a compile time error
Why?
Whenever you perform a binary operation between two operands of different types, one of the operands is promoted to the higher type. And then the result of the operation is of that type.
So, in your case, the byte type a is first promoted to an int, since 1 is an int type. And then after the addition operation, the result is of type int. Now, since you cannot assign an int to a byte, you need to do a typecast to remove the compiler error:
byte a = 2;
a = a + 1; // Error: Cannot assign an int value to byte
a = (byte)(a + 1); // OK
Now, in case of Compound Assignment Operator, the typecasting is done implicitly for you. The expression:
a += 1
is internally converted to:
a = (byte)(a + 1);
This is specified in JLS - §15.26.2 Compound Assignment Operator:
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.
Similar is the case with prefix increment operators, and postfix increment operators.
According to JLS - §15.15 Unary Operators:
The type of the prefix increment expression is the type of the variable.
a = a + 1;
That is due to type of the expression a = a + 1; . The LHS is promoted to int and then the addition is performed. You are then trying to assign the int value back to a byte without casting .
Refer the JLS 5.6.2
Widening primitive conversion (§5.1.2) is applied to convert either or both operands as specified by the following rules:
If either operand is of type double, the other is converted to double.
Otherwise, if either operand is of type float, the other is converted to float.
Otherwise, if either operand is of type long, the other is converted to long.
Otherwise, both operands are converted to type int.
a++;
++a;
Here the type is that of the type of the variable. Again as per the JLS 15.15
The type of the prefix increment expression is the type of the variable.
The type of the prefix decrement expression is the type of the variable.
a += 1;
As per the JLS 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.
Hence here a += 1; is equivalent to a = (byte)(a+1);, as it is done implicitly .

implicit conversion in java operator +=

I've found that java compile has a non-expected behavior regarding assignment and self assignment statements using an int and a float.
The following code block illustrates the error.
int i = 3;
float f = 0.1f;
i += f; // no compile error, but i = 3
i = i + f; // COMPILE ERROR
In the self assignment i += f the compile does not issue an error, but the result of the exaluation is an int with value 3, and the variable i maintains the value 3.
In the i = i + f expression the compiler issues an error with "error: possible loss of precision" message.
Can someone explain this behavior.
EDIT: I've posted this code block in https://compilr.com/cguedes/java-autoassignment-error/Program.java
http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.26.2
The Java Language Specification says:
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.
So i += f is equivalent to i = (int) (i + f).
i believe that the explicit i+f fails because of Narrowing primitive conversion. While in the first case the conversion on the right side passes because it is done according to Compound Assignment rules.

Java += meaning

I have seen this symbol/operator in a block of code:
a+=1;
But I cannot figure out what it does. Can someone help me please?
It is equivalent to
a = a + 1;
From the Java Language Specification:
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.
The last phrase is important if the left-hand side has side effects:
array[i++] += 1;
This is not equivalent to:
array[i++] = array[i++] + 1;
The first expression will increment i once. The second will increment i twice and will assign the right-hand value to a different element of array than will the first expression.
I should note that these kind of side-effect statements are not good programming form, despite the fact that you often find them used.
The cast is also important because the type of (E1) op (E2) may not be assignment-compatible with E1. For example, if a is of type short, then a++ is not equivalent to a = a + 1. The latter will not compile because the type of a + 1 is int and cannot be assigned to a short variable without a cast. That's why the spec in this case says that a++ is equivalent to a = (short) ((a) + (1)). The same thing goes if a is of type char or byte.
x += y;
is equivalent to
x = x + y;
There are similar operators for the other mathematical operations: -=, *=, /=. For example:
x *= y;
is equivalent to
x = x * y;
(EDIT: The above assumes there are no 'side-effects' in x; ie, preincrement or postincrement operators. Edited to reflect Ted Hopp's point)
It is shorthand for the following:
a = a + 1;
It means a = a + 1 i.e increment a.

Varying behavior for possible loss of precision

In Java, when you do
int b = 0;
b = b + 1.0;
You get a possible loss of precision error. But why is it that if you do
int b = 0;
b += 1.0;
There isn't any error?
That's because b += 1.0; is equivalent to b = (int) ((b) + (1.0));. The narrowing primitive conversion (JLS 5.1.3) is hidden in the compound assignment operation.
JLS 15.26.2 Compound Assignment Operators (JLS Third Edition):
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.
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);
This also explains why the following code compiles:
byte b = 1;
int x = 5;
b += x; // compiles fine!
But this doesn't:
byte b = 1;
int x = 5;
b = b + x; // DOESN'T COMPILE!
You need to explicitly cast in this case:
byte b = 1;
int x = 5;
b = (byte) (b + x); // now it compiles fine!
It's worth noting that the implicit cast in compound assignments is the subject of Puzzle 9: Tweedledum from the wonderful book Java Puzzlers. Here are some excerpt from the book (slightly edited for brevity):
Many programmers think that x += i; is simply a shorthand for x = x + i;. This isn't quite true: if the type of the result is wider than that of the variable, the compound assignment operator performs a silent narrowing primitive conversion.
To avoid unpleasant surprises, do not use compound assignment operators on variables of type byte, short, or char. When using compound assignment operators on variables of type int, ensure that the expression on the right-hand side is not of type long, float, or double. When using compound assignment operators on variables of type float, ensure that the expression on the right-hand side is not of type double. These rules are sufficient to prevent the compiler from generating dangerous narrowing casts.
For language designers, it is probably a mistake for compound assignment operators to generate invisible casts; compound assignments where the variable has a narrower type than the result of the computation should probably be illegal.
The last paragraph is worth noting: C# is a lot more strict in this regard (see C# Language Specification 7.13.2 Compound assignment).

Categories

Resources