Widening primitive conversions followed by boxing conversions [duplicate] - java

final byte b = 12;
Short s = b;
Integer i = b;
Program compiles fine for Short but for Integer compilation fails with "incompatible types" message.
I am having difficult time trying to understand this behavior. I could not find anything for this specific scenario..

I attempted to duplicate this with a wider group of assignment contexts:
final byte b = 12;
Byte b2 = b;
Character c = b; // Only an error if b isn't final
char c2 = b; // Only an error if b isn't final
Short s = b; // Only an error if b isn't final
short s2 = b;
Integer i = b; // Error, as indicated in the question
int i2 = b;
Long l = b; // Also an error
long l2 = b;
Float f = b; // Also an error
float f2 = b;
Double d = b; // Also an error
double d2 = b;
Assigning not just to a Integer, but also to a Float, a Long or a Double is also an error.
Interestingly, if the original declaration of b was NOT final, then assigning to a Character, a char, or a Short fails also.
Section 5.2 of the JLS sheds a little light on the subject of assignment contexts and their allowed conversions.
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.
This covers all of the conversions to wider primitive variables, which are always allowed, whether b is final or not. (That holds unless b is negative, in which case the assignment to an unsigned char (or Character) would fail.) Continuing:
In addition, if the expression is a constant expression (§15.28) of type byte, short, char, or int:
A narrowing primitive conversion may be used if the type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable.
A narrowing primitive conversion followed by a boxing conversion may be used if the type of the variable is:
Byte and the value of the constant expression is representable in the type byte.
Short and the value of the constant expression is representable in the type short.
Character and the value of the constant expression is representable in the type char.
Because b is final, the expression b is a constant expression, allowing it to be narrowed from the int constant expression 12 to byte, char, or short and then boxed to Byte, Character, or Short, but strangely, not to Integer or anything "above". The only possible explanation I can think of is that constant expressions that are subject to a primitive narrowing conversion aren't specifically allowed to be converted to Integer, Long, Float, or Double.
If b isn't final, then the narrowing followed by boxing isn't allowed, and a non-constant expression can't be promoted from byte to char either.

Related

Why final byte variable cannot be converted to Integer during auto-boxing?

There is no error when I try to autobox i2 to Byte,but when I do vise-versa(b1 to Integer),then an error occurs.
final byte b1 = 1;
Integer i1 = b1; //error
final int i2 = 1;
Byte b2 = i2;// no error
byte b3 = 1;
int i3 = b3; // no error
Can I suggest that you read JLS Sec 5.2, that I linked in my answer to your previous similar question.
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.
...
In addition, if the expression is a constant expression (§15.28) of type byte, short, char, or int:
A narrowing primitive conversion may be used if the type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable.
A narrowing primitive conversion followed by a boxing conversion may be used if the type of the variable is:
Byte and the value of the constant expression is representable in the type byte.
...
Taking your cases in reverse order:
byte b3 = 1;
int i3 = b3; // no error
Assigning a byte to an int is simply a widening conversion.
final int i2 = 1;
Byte b2 = i2;// no error
This is exactly the same as your previous question: you can assign a constant-valued int to a Byte, provided the value of that int fits into a byte.
final byte b1 = 1;
Integer i1 = b1; //error
You're trying to do a widening primitive conversion, followed by a boxing conversion. That's not one of the cases listed here, so it's an error.
You can fix this with an explicit widening cast:
Integer i1 = (int) b1; //ok

Java char to byte casting

I have been testing the char casting and I went through this:
public class Test {
public static void main(String a[]) {
final byte b1 = 1;
byte b2 = 1;
char c = 2;
c = b1; // 1- Working fine
c = b2; // 2 -Compilation error
}
}
Can anyone explain why it's working fine in 1 when I added a final to the byte?
When the variable is final, the compiler automatically inlines its value which is 1. This value is representable as a char, i.e.:
c = b1;
is equivalent to
c = 1;
In fact, according to this section on final variables, b1 is treated as a constant:
A variable of primitive type or type String, that is final and initialized with a compile-time constant expression (§15.28), is called a constant variable.
The conversion from byte to char is a widening and narrowing primitive conversion, as described in paragraph 5.1.4 of the Java Language Specification.
As the JLS describes, this is done via an intermediate step; the byte is converted to int via a widening primitive conversion and then the int is converted to char via a narrowing primitive conversion (see 5.1.3).
Paragraph 5.2 explains when a cast is necessary when you do an assignment:
... if the expression is a constant expression (§15.28) of type byte, short, char, or int:
A narrowing primitive conversion may be used if the type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable.
Your variable b1 is indeed a constant, but your variable b2 is not, so this rule applies for b1 but not for b2.
So: you can assign b1 to c because b1 is a constant and the value of the constant, 1, fits in a char, but you cannot assign b2 to c without a cast because b2 is not a constant.
Well , its because byte is a signed type while char is not, so u need to apply explicit type conversion for (2)
c = (char)b2;
also the final statement worked for 1 because prior to compilation , the compiler is able to confirm that there is no loss due to conversion since '1' is in the range of char , try putting '-1' with the same final statement in (1) you will again get a compilation error.
All this boils down to the type compatibility between signed and unsigned types..which needs to be done explicitly in java.

Rules for typecasting with byte

I was just playing around with type casting. Here's the code:
class Typecasting {
public static void main(String[] args) {
byte b = 3;
byte c = b++; // no error
byte d = b + 1; // error
byte e = b + b; // error
}
}
Why is there no error in the first line but in second?? Also when i do
f = b + 2;
I understand that b was automatically cast into int type and therefore f must be int type but when I do
e = b + b;
they both are byte type and their result is also in the range of a byte, so why can't e have byte data type? Is it due to the + binary operator?
Why is there no error in the first line but in second?
Because that's the way the language is defined. There's no byte + byte operator, which is why the third line fails - both operands are promoted to int automatically. From section 15.18.2 of the JLS:
The binary + operator performs addition when applied to two operands of numeric type, producing the sum of the operands.
...
Binary numeric promotion is performed on the operands (§5.6.2).
Now binary numeric promotion always ends up with a value of int, long, float or double... int in this case. So the operation is to add two int values together, and the result is an int.
For b++, however, the type is still byte. It's a postfix increment expression (section 15.14.2):
The type of the postfix increment expression is the type of the variable.
On a related note, this would be okay:
b += 3;
That's a compound assignment (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 casting part, which is why it works.
+(byte, byte) returns an int per the rules of the language.
The relevant section of the spec is §5.6.2:
When an operator applies binary numeric promotion to a pair of operands, each of which must denote a value that is convertible to a numeric type, the following rules apply, in order, using widening conversion (§5.1.2) to convert operands as necessary:
If any of the operands is of a reference type, unboxing conversion (§5.1.8) is performed. Then:
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.
Whereas for ++, the type of the result is the type of the operand. From the JLS, §15.14.2:
The type of the postfix increment expression is the type of the variable.
Thus, for b++, the result is a byte and so is assignable to c.
byte d = b+1;
In here you are assign int value(b+1) to byte d. When you added a int vale to byte value it becomes int value.
byte c = b++; //No error;First Line
This conforms to JLS 15.14.2
The type of the postfix increment expression is the type of the variable. The result of the postfix increment expression is not a variable, but a value.
byte d = b+1; //error;Second Line
byte e = b+b; //error;Third line
This conforms to JLS 15.18.2
The binary + operator performs addition when applied to two operands of numeric type, producing the sum of the operands.
The type of an additive expression on numeric operands is the promoted type of its operands.
If this promoted type is int or long, then integer arithmetic is performed.
Read about type promotion in 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.
in line first ++operator returns always operand type result..so it gives no error
in second line here 1 is an integer type because all integer type literals is int by default
in third line +(byte,byte) returns the integer type that why it will forces you to type cast it into int.
--> in third line there is always a probability to overflow in byte type..
* if your byte type is defined as constant then it will not force you to typecast because it will alwayz be in range.
When Java performs a sum, it first "transforms" both operands to int or long (depending of the operands) and the result will be an integer, which will try to assign to your variable of type byte, as it "realizes" the result wont fit in a byte, then it will "complain" and you wont be even able to compile this.

Casting characters in Java

I am learning Java. I found that expressions often have to be cast to a certain type in order to do it right. For example, during arithmetic evaluation, bytes are promoted to integers, so the following expression will throw an error:
byte b = 10;
int i;
i = b*b; //ok, assigning an integer evaluation to an integer variable
b = b*b; // throws error, coz assigning integer evaluation to byte variable
Now, I know that assigning an integer to a character variable is all right: char a; a = 88; is okay. However, if I do this:
char c2 = 'b', c3 = 'c';
c2 = c2 + c3; //throws error
c2 = (char)(c2 + c3); //works fine
Why does it throw an error when not casted? After all, the right hand side is still an integer, so assigning an integer to a character variable should work just fine.
In c2 + c3, both operands are implicitly widened to int, so the result of the addition is also an int.
JLS §15.18.2. Additive Operators (+ and -) for Numeric Types:
Binary numeric promotion is performed on the operands (§5.6.2).
JLS §5.6.2. Binary Numeric Promotion:
When an operator applies binary numeric promotion to a pair of operands, each of which must denote a value that is convertible to a numeric type, the following rules apply, in order:
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.
You therefore end up with an int. Assigning it to a char variable requires an explicit cast.
You say:
Since integer value can be assigned to a character variable...
Only constant integer expressions can be assigned to a char variable without a cast.
JLS §5.2. Assignment Conversion:
In addition, if the expression is a constant expression (§15.28) of type byte, short, char, or int:
A narrowing primitive conversion may be used if the type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable.
This automatic narrowing conversion doesn't apply here. You need an explicit cast.
It does not necessarily work fine to assign an int to a char. Chars are only 16 bit and ints are 32 bit, so the there might be an overflow.
In general Java only allows assignment of primitives values without cast if no overflow can occur as a result of the assignment.
char c2 = 'b', c3 = 'c';
c2 = c2 + c3; //throws error
c2 = (char)(c2 + c3); //works fine
when you doing c2+ c3
ASCII value of these chars are added which returns a int result.
when you are assigning int result to char it gives error.
Assigning an int 88 to a char works, because the compiler can determine the value.
The case c2 = c2 + c3 cannot be handled by the compiler. The value c2 + c3 must be evaluated at run time. Therefore, the compiler cannot determine the actual char value that has to be assigned.
char is 2 bytes and int is 4 bytes. When you write char c = 1; it does not mean that 1 is int, it's just a constant for javac and javac knows that 1 fits into char. But c2 = c2 + c3; is arithmetic operation, javac will interprets it as (int)c2 + (int)c3 and this produces int result. int does not fit into char so javac warns you that there may be lost of precision.
Have a look at thread:
Integer arithmetic in Java with char and integer literal
The reason seems to be that "c2 = c2 + c3;" cannot be checked by the compiler (it is executed in runtime) whereas "char a; a = 88;" is directly done by the compiler.
An example for further clarification:
char c1 = Character.MAX_VALUE;
char c2 = Character.MAX_VALUE;
char c3 = (char) (c1 + c2);
int i3 = c1 + c2;
System.out.printf("After casting to char: %s, the int value: %s%n", (int) c3, i3);
So with casting we actually got a wrong mathematical result.
There is a certain limit as to how much value each basic type can hold in java.Assigning the result of an arithmetic operation can produce an unpredictable result at run-time ,which JVM is unsure if char can hold,therefore the compilation error.

Conversion between character and int in Java

You cannot convert from int to char, so this would be illegal
int i = 88; char c = i;,
However this is allowed char c = 88;.
Isn't a plain number and int literal? How is this allowed?
char is effectively an unsigned 16-bit integer type in Java.
Like other integer types, you can perform an assignment conversion from an integer constant to any integer type so long as it's in the appropriate range. That's why
byte b = 10;
works too.
From the JLS, section 5.2:
In addition, if the expression is a
constant expression (§15.28) of type
byte, short, char or int :
A narrowing primitive conversion may
be used if the type of the variable is
byte, short, or char, and the value of
the constant expression is
representable in the type of the
variable.
A narrowing primitive
conversion followed by a boxing
conversion may be used if the type of
the variable is :
Byte and the value
of the constant expression is
representable in the type byte.
Short
and the value of the constant
expression is representable in the
type short.
Character and the value of
the constant expression is
representable in the type char.
Actually, converting from int to char is legal, it just requires an explicit cast because it can potentially lose data:
int i = 88;
char c = (char) i;
However, with the literal, the compiler knows whether it will fit into a char without losing data and only complains when you use a literal that is too big to fit into a char:
char c = 70000; // compiler error
Its because the literals for integer or smaller than int as byte ,short and char is int. Understand the following in this way.
code:
byte a = 10;//compile fine
byte b= 11;//compile fine
byte c = a+b;//compiler error[says that result of **a+b** is **int**]
the same happens for any mathematical operations as of 'Divide', 'multiply', and other arithmetic operation. so cast the result to get the literal in desired data type
byte c = (byte)(a+b);
So that the same reason why the value int need to have primitive cast to change the value in char.
Hope this make some sense.

Categories