I have confusion in Number Wrapper Class in Java.
These two assignments look symmetric - a char is assigned to Integer, and an int is assigned to Character. However, the first assignment
Integer i = 'a';
gives Compilation Error, while the second assignment
Character ch2 = 97;
is allowed. Why?
Although int i = 'a' works fine, converting the same to Integer is not allowed, because it requires a boxing conversion.
Java's boxing conversion is defined only for eight cases:
From type boolean to type Boolean
From type byte to type Byte
From type short to type Short
From type char to type Character
From type int to type Integer
From type long to type Long
From type float to type Float
From type double to type Double
Since 'a' is a char literal, Java does not allow conversion from char to Integer: a character literal is always of type char.
However, when you write
Character ch2 = 97;
Java compiler sees that 97 is in the valid range for char (i.e. 0..65535), so it treats 97 as char, not int, and allows the boxing conversion. Trying the same with an out-of-range constant produces an error:
Character ch3 = 65536; // error: incompatible types: int cannot be converted to Character
The rules for boxing conversions can be found in the Java Language Spec, chapter 5.1.7
Boxing conversion converts expressions of primitive type to corresponding expressions of reference type. Specifically, the following nine conversions are called the boxing conversions:
... followed by a list of valid conversions from primitive types to reference types.
The point is: in any case, a conversion must take place.
If you had
int a = '97'
that is fine; as that is a widening conversion (sectin 5.12 in the JLS). But that case
Integer i = '97'
isn't listed as "valid" conversion for Auto-boxing. In other words: the JLS doesn't allow for it; and this the compiler doesn't do it either.
...
Integer i = 'a' just wraps int i ='a'. Since 'a' is not an int it throws an error.
Likewise, Character ch2 = 97 wraps char ch2 = 97. However, 97 is an valid char! It represents the character "a". Example:
System.out.println((char) 97); //Output: a
This is because when setting a character to an integer it's returning the ASCII code number which is a primitive int.
Related
Could you explain in a detailed manner why the expected result is not correct? As most of the readers expect that the output is Byte Char Int Byte, but of course it is not the correct answer.
public class Tester {
public static String test(byte b) {
return "Byte ";
}
public static String test(char c) {
return "Char ";
}
public static String test(int i) {
return "Int ";
}
public static void main(String[] args) {
byte b = 0;
char c = 'A';
System.out.print(test(true ? b : c));
System.out.print(test(false ? b : c));
System.out.print(test(true ? 0 : 'A'));
System.out.print(test(false ? 'A' : (byte)0));
}
}
The correct result is Int Int Char Int.
This is because overload resolution depends on the compile-time type of the argument expressions, not the runtime type of the argument. See JLS 15.12.2.2:
The argument expression true ? b : c has the type int, so the Int method is called. This is specified in the JLS 15.25.2, where it is said that if none of the special cases match (none of them do here), then the type of the conditional expression is the type after applying numeric promotion to the second and third operands:
An expression appears in a numeric choice context if the expression is one of the following:
The second or third operand of a numeric conditional expression
[...]
Numeric promotion determines the promoted type of all the expressions in a numeric context. [...] The rules are as follows:
[...] (none of these rules match in the case of a short and a byte expression)
Otherwise, none of the expressions are of type double, float, or long. In this case, the kind of context determines how the promoted type is chosen.
In a numeric choice context, the following rules apply:
[...] (none of these rules match in the case of a short and a byte expression)
Otherwise, the promoted type is int, and all the expressions that are not of type int undergo widening primitive conversion to int.
This is why true ? b : c is of type int. The same goes for false ? b : c and false ? 'A' : (byte)0. It doesn't matter what the condition is. The type of the expression is determined by the types of the second and third operands.
As for test(true ? 0 : 'A'), this can be explained by a rule that I omitted above, just before the last "Otherwise..."
Otherwise, if any expression is of type char, and every other expression is either of type char or a constant expression of type int with a value that is representable in the type char, then the promoted type is char, and the int expressions undergo narrowing primitive conversion to char.
In this case, the expression "0" is a "a constant expression of type int with a value that is representable in the type char".
One way to think about the type of a conditional expression, is that it's the smallest type that the expressions of both of the branches can "fit". For a byte b and a char c, int is the smallest type that can fit both of them. For 0, and 'A', char is enough to fit both, as it is known that 0 is in the range of char.
For the type of a conditional expression in Java, always consult the JLS charts (Section 15.25).
3rd →
byte
short
char
int
2nd ↓
byte
byte
short
bnp(byte,char)
byte | bnp(byte,int)
Byte
byte
short
bnp(Byte,char)
byte | bnp(Byte,int)
short
short
short
bnp(short,char)
short | bnp(short,int)
Short
short
short
bnp(Short,char)
short | bnp(Short,int)
char
bnp(char,byte)
bnp(char,short)
char
char | bnp(char,int)
where the notation "bnp(x, y)" means the type that is the result of binary numeric promotion of x and y, and the form "T | bnp(..)" means that if one operand is a constant expression of type int and may be representable in type T, then binary numeric promotion is used if the operand is not representable in type T.
For the first 2 calls, we have a char and a byte, so the result is "bnp(byte, char)", or the type int.
For the third call, we have a constant expression of type int and a char, and the value is representable as a char (it's 0), so the result is the type char.
For the fourth call, we have a byte and a char, so we back to "bnp(byte,char)", which is the type int.
The ternary operator converts its expressions to a common type in order for a variable to store its result.
Imagine if you had an expression like this:
char v = <myCondition> ? 'a' : 0L;
The previous statement would make sense only if the condition was true, but what about if this was false and 0L was to be returned? long and char are not the same data type. So, the compiler needs to find a common type, without losing precision, in order to allow both expressions to be returned and assigned to a variable suitable for both of them. The resulting data type is picked up regardless the condition's outcome.
In this case, long cannot be cast to int and then char as it would lose precision whereas char could be cast to int and then promoted to long. This is why the previous statement can be correctly written like so:
long v = <myCondition> ? 'a' : 0L;
In your example, the first ternary operator has a byte and a char, a byte can be promoted to an int while a char can be cast to an int. The other way around, where a char would be cast to a byte is not possible as this would cause a precision loss (a char occupies two bytes). By applying the same logic of promotion, you can also explain the remaining cases. Except for the third one, where you should apply the resolving table from the previously provided link to the documentation.
Table 15.25-A in the Java language specification describes what type the result of a ?: operator is, depending on the contributing types.
Your first example involves byte and char, so the result is a bnp (binary numeric promotion) of these, and for anything but double, float, or long, the resulting type is int.
Same thing for the others.
I was surprised to see this code work. I thought that char and int were two distinct data types in Java and that I would have had to cast the char to an int for this to give the ascii equivelent. Why does this work?
String s = "hello";
int x = s.charAt(1);
System.out.println(x);
A char can be automatically converted to an int. See JLS 5.1.2:
The following 19 specific conversions on primitive types are called the widening primitive conversions:
...
char to int, long, float, or double
...
A widening conversion of a signed integer value to an integral type T simply sign-extends the two's-complement representation of the integer value to fill the wider format. A widening conversion of a char to an integral type T zero-extends the representation of the char value to fill the wider format.
(emphasis added)
char and int are two distinct types, but this works because an int has more precision than a char. That is, every value of char can be represented as an int so no data is lost in the cast.
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.
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.
Can someone explain to me why the following code compiles OK in Java?
char c = 'a' + 10;
Why is this not equivalent to the following, which does not compile?
int i = 10;
char c = 'a' + i;
The Java Language Specification (section 3.10.1) states "An integer literal is of type long if it is suffixed with an ASCII letter L or l (ell); otherwise it is of type int (§4.2.1)." Section 4.2.2 refers to "The numerical operators, which result in a value of type int or long." So the result of the addition should, in my understanding, be an int, which cannot be assigned to the char variable c.
However, it compiles fine (at least in Sun JDK 1.6.0 release 17 and in Eclipse Helios).
Rather an artificial example perhaps, but it is used in an introductory Java course I have been teaching, and it now occurs to me that I don't really understand why it works.
It is because the compiler can check that it ('a' + 10) is within the bounds of a char whereas it cannot (in general) check that 'a' + <an integer> is within the bounds.
'a' + 10 is a compile-time constant expression with the value of 'k', which can initialise a variable of type char. This is the same as being able to assign a byte variable with a literal integer in [-128, 127]. A byte in the range of [128, 255] may be more annoying.
char is actually an unsigned 16-bit integer with a range 0-65535. So you can assign any integer literal in that range to a char, e.g., "char c = 96", which results in "c" holding the character "a". You can print out the result using System.out.println(c).
For the constant expression on the right-hand-side of "char c = 'a' + 10", 'a' is promoted to int first because of the Java numeric promotion rules and the integer value is 96. After adding 10 to it, we get a literal integer 106, which can be assigned to a char.
The right-hand-side of "char c = 'a' + i" is not a constant expression and the expression result assignment rule requires an explicit cast from int to char, i.e., "char c = (char) ('a' + i)".
This code should works:
int i = 10;
char x = (char)('a' + i);
The constant is of a different type (I know the spec says that 10 should be an int, but the compiler doesn't see it that way).
In char c = 'a' + 10, 10 is actually considered a constant variable of type char (so it can be added to a). Therefore char c = char + char works.
In int i = 10;
char c = 'a' + i; You are adding a char to an integer (an integer can be much bigger than a char, so it chooses the bigger data type [int] to be the result a.k.a: 'a' + i = int + int). So the result of the addition is an integer, which cannot fit into the char c.
If you explicitly casted i to be a char (e.g.: char c = 'a' + (char)i;) it could work or if you did the opposite (e.g.: int c = (int)'a' + i;) it would work.
According to Java specification as of 2020 for widening and narrowing conversions of integral values in expressions:
"In a numeric arithmetic context ... the promoted type is int,
and any expressions that are not of type int undergo widening
primitive conversion to int"
In assignment context:
"...if the expression is a constant expression of type byte, short,
char, or int:
• A narrowing primitive conversion may be used if the
variable is of type byte, short, or char, and the value of the
constant expression is representable in the type of the variable."
So, in char c = 'a' + 10; the left constant value is a charand the right constant value is int fitting into a char. While there is an assignment and int 10 fits into char, int gets converted to char. And the overall result of addition is char.
And in char c = 'a' + i; (where int i = 10;) the i is not constant, so, notwithstanding the assignment, the char 'a' is promoted to int and the overall result is int. Thus, the assignment is erroneous without an explicit typecast.
Note, that the following original answer is wrong (it cites treatment in numeric choice contexts, like in switch statement):
According to Java specification for widening and narrowing conversions in expressions:
If any expression is of type int and is not a constant expression,
then the promoted type is int, and other expressions that are not of
type int undergo widening primitive conversion to int.
...
if any expression is of type char, and every other expression is
either of type 'char' or a constant expression of type 'int' with a
value that is representable in the type 'char', then the promoted type
is char, and the int expressions undergo narrowing primitive
conversion to char.
So, in char c = 'a' + 10; the left expression is a char and the right constant expression is int fitting into a char. So, the constant gets converted to char. And the overall result is char.
And in char c = 'a' + i; (where int i = 10;) the right expression is not constant, so the the char 'a' is promoted to int and the overall result is int.