In Java, the following is allowed:
char c = 'A' + 1;
Here, c will hold the value 'B'. Above, first the expression is evaluated. So 'A' gets converted to 65, the whole expression evaluates to 66, and then 66 is converted to 'B' since we are storing the value in a char.
The following, however, gives a compile-time error:
char c = 'A';
c = c + 1;
What is the explanation for how Java views the expressions differently? By the way, the following works fine too:
char c = 'A';
c++;
The first example (which compiles) is special because both operands of the addition are literals.
A few definitions to start with:
Converting an int to char is called a narrowing primitive conversion, because char is a smaller type than int.
'A' + 1 is a constant expression. A constant expression is (basically) an expression whose result is always the same and can be determined at compile-time. In particular, 'A' + 1 is a constant expression because the operands of + are both literals.
A narrowing conversion is allowed during the assignments of byte, short and char, if the right-hand side of the assignment is a constant expression:
In addition, if the expression [on the right-hand side] 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.
c + 1 is not a constant expression, because c is a non-final variable, so a compile-time error occurs for the assignment. From looking at the code, we can determine that the result is always the same, but the compiler isn't allowed to do that in this case.
One interesting thing we can do is this:
final char a = 'a';
char b = a + 1;
In that case a + 1 is a constant expression, because a is a final variable which is initialized with a constant expression.
The caveat "if […] the value […] is representable in the type of the variable" means that the following would not compile:
char c = 'A' + 99999;
The value of 'A' + 99999 (which is 100064, or 0x186E0) is too big to fit in to a char, because char is an unsigned 16-bit integer.
As for the postfix ++ operator:
The type of the postfix increment expression is the type of the variable.
...
Before the addition, binary numeric promotion* is performed on the value 1 and the value of the variable. If necessary, the sum is narrowed by a narrowing primitive conversion and/or subjected to boxing conversion to the type of the variable before it is stored.
(* Binary numeric promotion takes byte, short and char operands of operators such as + and converts them to int or some other larger type. Java doesn't do arithmetic on integral types smaller than int.)
In other words, the statement c++; is mostly equivalent to:
c = (char)(c + 1);
(The difference is that the result of the expression c++, if we assigned it to something, is the value of c before the increment.)
The other increments and decrements have very similar specifications.
Compound assignment operators such as += automatically perform narrowing conversion as well, so expressions such as c += 1 (or even c += 3.14) are also allowed.
char to int conversion is called widening conversions. In widening conversions, values do not lose information about the overall magnitude of a numeric value where as int to char conversion is called narrowing conversions. With narrowing conversion you may lose information about the overall magnitude of a numeric value and may also lose precision.
For more information on primitive conversions refer this document.
It is because the compiler can check that it ('A' + 1) is within the bounds of a char whereas it cannot (in general) check that c + <an integer> is within the bounds.
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 when you perform
c= c+1;//compiler error
Its the result of c+1 is int not a char . so compiler give a compile time error for the same.
so you need to provide a primitive cast to change the literal to char data type.
Hope this example provide some understanding..
Related
Consider the below code snippet:
// automatic casting works for int to byte conversion as integer literal 127
// is in the range for byte
byte b1 = 127; //OK
// automatic casting doesn't work for long to int conversion
// even if long literal is in the range of int.
int i5 = 100L; // NOT OK - compilation error
Is there any explanation for such behavior?
Why is explicit conversion not needed in the case of int to byte, but needed for long to int?
The How does Java convert int into byte? question is different. It is about an issue in implicit conversion of int to byte when the int value is out of range.
Widening conversions (eg. byte to int) are generally accepted implicitly by the Java compiler, as there's no loss of information (the range of int is greater than that of byte).
Narrowing conversions (eg. long to int, as in your case) can cause a loss of information, so are generally required to be explicitly casted.
See this similar question, and this. A relevant piece of the Java Language Specification:
Assignment conversion occurs when the value of an expression is assigned (§15.26) to a variable: the type of the expression must be converted to the type of the variable.
...
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.
(emphasis mine)
Some confusion stems from the fact that we're dealing in particular with constant expressions, as we're using numeric literals. The above spec also needs some careful reading.
To clear some things up and directly answer some of the OP's queries:
Why implicit narrowing is supported for one type of narrowing and not the other?
Ie. why does byte b1 = 127 work implicitly, while int i5 = 100L not?
byte b1 = 127 performs an implicit conversion as (cf. the bold text in the above quote), "the value of the constant expression is representable in the type byte". That is, 127 is representable by a byte, so the conversion is implicit. If you try byte b1 = 128, you'll get an error about incompatible types, as 128 isn't representable by byte. The only reason we're allowed an implicit cast here at all is because we're using a constant expression.
We don't get an implicit conversion in int i5 = 100L (even though 100 is in the range of int) as that's simply not listed in the allowed implicit conversions (the variable's type, int, is not one of byte, short, or char).
We also don't get an implicit conversion in byte a = 0L, this time as the constant expression is of type long, not of type byte, short, char, or int.
How will a normal programmer know which narrowing conversion is allowed implicitly?
The implicit narrowing conversions only occur when you're assigning a constant expression to a variable. In these cases, implicit conversions are good as we don't want to be writing code like byte b = (byte)0 all the time. At the same time, we do want to be warned if we write something like byte b = 128, as that doesn't have intuitive behaviour.
When we're not assigning constant expressions (so eg. int x = 0; byte b = x;), we always want to be warned when we're doing a potentially lossy conversion, as they're dangerous - so explicit conversions in this case also make sense.
I've read most of the threads on this site concerning typecasting (please do not link me to any more or mark this as a duplicate), and would like an explanation on some of these specific cases.
1)
char x='a';
char y = 'b';
In this case, we are not allowed to do
char z = x + y; \\case 1a
But we can do
char z='a'+'b'; \\ case 1b
I read on this site that the addition of two chars results in an int, and that is why we cannot do 1a, but if so, why can we do 1b.
2)
Why is this allowed
byte byteVar = 42;
The literal 42 is an int, so why are we allowed to store it in a byte without casting even though 42 is in the range of byte. If this can be done, why can't 1a be done.
3)
The following returns true
System.out.println(6.0000000000000000000000000000000001 == 6.0);
The RHS is treated as a literal double. I'm guessing that the LHS is treated as a literal double as well, but it definitely exceeds the precision allowed for a double. Why isn't an error thrown and how are the two things compared.
I would greatly appreciate any guidance on this. Thank you.
The first two are handled by the same case - 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.
The third is simply a case of regular conversion from a literal in source code to a double: the nearest exact double to 6.0000000000000000000000000000000001 is 6.0. So if you had:
double d = 6.0000000000000000000000000000000001;
that would assign a value of 6.0 to d, as that's the closest representable double to the value specified in the source code.
This question already has an answer here:
Why byte and short values are promoted to int when an expression is evaluated [duplicate]
(1 answer)
Closed 7 years ago.
When we do addition,subtractions,divide(etc..)in byte short data types the result is coming from int value...why is that...?
e.g:- in above code does not compile because the result in c is coming from int why is that happen...??? when we try to print c using system out it came error because result is in int
public class A {
public static void main(String[] args) {
byte a = 12;
byte b = 10;
byte c = a + b;
System.out.println(c);
}
}
According to java doc the when we perform operations on two bytes there is probability that the output would be a number which can't be handled by byte, so it was decided by creators of java to auto cast it to Integer. So it's like this.
This is well answered here, starting from an identical scenario :) http://www.coderanch.com/t/499127/java/java/Adding-bytes
To quote http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#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.
The spec says:
https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.18.2
The binary + operator performs addition when applied to two operands of numeric type, producing the sum of the operands.
The binary - operator performs subtraction, producing the difference of two numeric operands.
Binary numeric promotion is performed on the operands (§5.6.2).
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
The type of an additive expression on numeric operands is the promoted type of its operands.
https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-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:
If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8).
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.
If you combine the two you see that any operand in an addition will be promoted to either int or long and any primitive smaller than int will be promoted to int. The result of the operation is the type of both operands - which it int.
The reasoning behind that is most probably that int is the most common datatype which reduces probability of overflow while not requiring as much space as a long. The space argument probably isn't as generally valid as it used to be anymore but that part of the spec was defined like in 1995 and an inherent part of the language (which you can't change without breaking compatibility).
Any Binary arithmetic operations we performed on char & byte datatype(and short) promote it to int.
For more details please refer JLS 5.6.2.
This works:
final byte a = 12;
final byte b = 10;
byte c = a + b;
Or this:
byte a = 12;
byte b = 10;
byte c = (byte)( a + b);
public class CharAndIntTest {
public static void main(String[] args) {
char i='1';
int ii=65;
i=ii;
}
}
An int has larger capacity than a char, so the conversion is not guaranteed to work. The possible range in the value of a char is 0 to 65535, but an int can be anywhere from -2147483648 to 2147483647.
However, there are other considerations other than raw storage size. char is an unsigned type (Java's only built-in unsigned type, actually), so even short and byte variables still need a conversion to char as they may be negative. short and char are both 16 bit, but since short is signed its range is different from that of char (range of short being from -32,768 to 32,767).
The most important thing to learn here is that Java only lets implicit conversions take place if it can be absolutely determined that the conversion will succeed. This applies to primitives, objects, generics, and pretty much everything.
You can force the conversion to happen using a cast:
char i='1';
int ii=65;
i=(char)ii; //explicit cast tells the compiler to force the int into a char
The assignment conversion (JLS, Section 5.2) allows a primitive widening conversion, but not a primitive narrowing conversion:
Assignment conversion occurs when the value of an expression is
assigned (§15.26) to a variable: the type of the expression must be
converted to the type of the variable.
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.
So you must cast to perform a primitive narrowing conversion, unless
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.
So assigning a literal will work:
char a = 65;
chars are 16-bit unsigned numbers, so they got a maximum of 65,635.
ints are 32-bit signed numbers,s o they got a maximum of 2,147,483,647.
So something is bound to go wrong if you sign an int to a char, for all values >= 65,635.
A char is a 16-bit datatype while an int is a 32-bit datatype. Thus, some possible int values can't be represented with a char without dropping some data (truncation). The compiler creates an error message to warn you about the possible problems caused by accidental truncation.
You can still assign int values to char if you do the appropriate casting:
int i=500;
char c = (char)i;
You can even assign illegal char values like this, although the result will still be truncated:
int i = 65555;
char c = (char)i; //still compiles and doesn't cause an exception, but data is lost
i = c; //i's value is 19
Why its printing X88
public static void main(String [] args)
{
char x = 'X';
int i = 0;
System.out.print(true ? x : 0);
System.out.print(false ? i : x);
}
In the first print statement, the type of the conditional expression is char (i.e. 'X') because this part of section 15.25 of the JLS (which is about the type of a conditional expression) applies:
If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression of type int whose value is representable in type T, then the type of the conditional expression is T.
So the first statement prints "X".
In the second print statement, that part doesn't apply because i isn't a constant expression. Instead, this section applies:
Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands. Note that binary numeric promotion performs unboxing conversion (§5.1.8) and value set conversion (§5.1.13).
Binary numeric promotion converts the char 'X' to an int (88) and the second statement prints "88" - hence the overall result of "X88".
It's explained in §15.25 of the JLS.
Otherwise, if the second and third
operands have types that are
convertible (§5.1.8) to numeric types,
then there are several cases:
This applies to both statements, since both char and int are numeric types, and thus 5.1.8 trivially applies. So we look at the rules below (excerpted):
[...]
If one of the operands is of type T where T is byte, short, or
char, and the other operand is a
constant expression of type int whose
value is representable in type T, then
the type of the conditional expression
is T.
This applies to the first, since 0 is a constant expression of type int. T is , char, which is why it prints as X.
[...]
Otherwise, binary numeric promotion (§5.6.2) is applied to the
operand types, and the type of the
conditional expression is the promoted
type of the second and third operands.
Note that binary numeric promotion
performs unboxing conversion (§5.1.8)
and value set conversion (§5.1.13).
This applies to the second, since nothing else does. :) The relevant rule from §5.6.2 is simply:
Otherwise, both operands are converted to type int.
The Unicode code point for 'X' is 88.
For the first statement System.out.print(false ? x : 0); the compiler thinks that you want to print a char so 0 will be considered as a char (during compilation).
When you do System.out.print(false ? i : x); the compiler thinks that you want to print an integer, so it will try to convert (widen) 'X' into an integer which is 88.
An interesting thing is that if you try to do this :
System.out.print(true ? x : i);
As i cannot be considered as a char (it would require narrowing) it will print 88
Another thing which is interessant here :
System.out.print(true ? x : 65536);
This time the value 65536 can't be considered as a char at compile time (the max value for a char is 65535 [#See Character.MAX_VALUE]) so it will be considered as an int, so x will be too.
To summarize, if one of the operand is an int and the other a char, there are two possible conversions :
The int is narrowed into a char (loss of precision)
The char is widen into an int (no loss of precision)
To avoid useless loss, Java will chose the second option.
When you do System.out.print(false ? x : 0);, the 0 is converted at compilation time into a char (because the compiler knows that there won't be loss of precision).
Resources :
JLS - Widening Primitive Conversion
Because the expression false ? i : x is being seen as returning an integer by the compiler. Since you are using a char the 'X' is 88 in ASCII and it is being seen as an integer since they are compatible.
Personally, I think there should be at least a compiler warning here.
In the second ternary If, the parameters must be of the same type, otherwise one of the two is implicitly converted.
In your example, x is converted from char to int, and 'X' has an ASCII code of 88.
Others have already answered why this happens.
If you want X to be printed, cast the expression value back to char as:
System.out.print((char)(false ? i : x)); // prints X
or you can also cast i to char so that the type of expression becomes char as:
System.out.print(false ? (char)i : x); // prints X