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.
Related
I have seen it discussed somewhere that the following code results in obj being a Double, but that it prints 200.0 from the left hand side.
Object obj = true ? new Integer(200) : new Double(0.0);
System.out.println(obj);
Result: 200.0
However, if you put a different object in the right hand side, e.g. BigDecimal, the type of obj is Integer as it should be.
Object obj = true ? new Integer(200) : new BigDecimal(0.0);
System.out.println(obj);
Result: 200
I presume that the reason for this is something to do with casting the left hand side to a double in the same way that it happens for integer/double comparisons and calculations, but here the left and right sides do not interact in this way.
Why does this happen?
You need to read section 15.25 of the Java Language Specification.
In particular:
Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:
If one of the operands is of type byte or Byte and the other is of type short or Short, then the type of the conditional expression is short.
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.
If one of the operands is of type Byte and the other operand is a constant expression of type int whose value is representable in type byte, then the type of the conditional expression is byte.
If one of the operands is of type Short and the other operand is a constant expression of type int whose value is representable in type short, then the type of the conditional expression is short.
If one of the operands is of type; Character and the other operand is a constant expression of type int whose value is representable in type char, then the type of the conditional expression is char.
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).
So binary numeric promotion is applied, which starts with:
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.
That's exactly what happens here - the parameter types are converted to int and double respectively, the second operand (the third in the original expression) is then of type double, so the overall result type is double.
Numeric conversion in the conditional operator ? :
In the conditional operator a?b:c, if both b and c are different numeric types, the following conversion rules are applied at compile time to make their types equal, in order:
The types are converted to their corresponding primitive ones, which is called unboxing.
If one operand were a constant int (not Integer before unboxing) whose value is representable in the other type, the int operand is converted into the other type.
Otherwise the smaller type is converted into the next greater one until both operands have the same type. The conversion orders are:
byte -> short -> int -> long -> float -> double
char -> int -> long -> float -> double
Eventually the whole conditional expression gets the type of its second and third operands.
Examples:
If you combine char with short, the expression becomes int.
If you combine Integer with Integer, the expression becomes Integer.
If you combine final int i = 5 with a Character, the expression becomes char.
If you combine short with float, the expression becomes float.
In the question's example, 200 is converted from Integer into double, 0.0 is unboxed from Double into double and the whole conditional expression becomes becomes double which is eventually boxed into Double because obj is of type Object.
Example:
public static void main(String[] args) {
int i = 10;
int i2 = 10;
long l = 100;
byte b = 10;
char c = 'A';
Long result;
// combine int with int result is int compiler error
// result = true ? i : i2; // combine int with int, the expression becomes int
//result = true ? b : c; // combine byte with char, the expression becomes int
//combine int with long result will be long
result = true ? l : i; // success - > combine long with int, the expression becomes long
result = true ? i : l; // success - > combine int with long, the expression becomes long
result = true ? b : l; // success - > combine byte with long, the expression becomes long
result = true ? c : l; // success - > char long with long, the expression becomes long
Integer intResult;
intResult = true ? b : c; // combine char with byte, the expression becomes int.
// intResult = true ? l : c; // fail combine long with char, the expression becomes long.
}
This question already has answers here:
Ternary operator casts integer
(3 answers)
Closed 5 years ago.
I recently come accross the scenario where in first syso() charcter is working fine but in second syso() it is printing ASCII code.
public class Test{
public static void main(String[] args) {
char x = 'A';
char y= 'B';
int m = 0;
System.out.println(true ? x : 0);//Working fine prints A
System.out.println(true ? y : 0);//Working fine prints B
System.out.println(false ? 0 : y);//Working fine prints B
System.out.println(false ? m : x);// Here it prints 65 why ?
}
}
I really want to know why it is printing ascii code in second syso() ? Please help
The issue is in the type of false ? m : x, which ends up being int, not char.
As per JLS section 15.25.2 (emphasis and [] note mine):
The type of a numeric conditional expression is determined as follows:
If the second and third operands have the same type, then that is the type of the conditional expression.
...
Otherwise [if none of the above rules hold], 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.
Where binary numeric promotion's relevant rule is (emphasis mine):
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.
Therefore in:
char x = ...;
int m = ...;
The expression condition ? m : x is promoted to int, and System.out.println(int) is called, and it prints it as a number.
You'd have to explicitly cast m or the whole expression to a char, e.g.:
System.out.println((char)(false ? m : x));
Or:
System.out.println(false ? (char)m : x);
As for your condition ? x : 0 and condition ? 0 : x forms, one of the rules (that I omitted above) from 15.25.2 is:
If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression (§15.28) of type int whose value is representable in type T, then the type of the conditional expression is T.
0 fits this description. x is a char, 0 fits in a char, the type of the conditional is therefore char and the character is printed.
I found below puzzle in Java Puzzlers,
public class DosEquis {
public static void main(String[] args) {
char x = 'X';
int i = 0;
System.out.print(true ? x : 0);
System.out.print(false ? i : x);
}
}
I tried this code and run it as well but output was not came as per my guess, my guess output should be : XX but in reality output is : X88
I tried lot to understand but i couldn't,can anybody give us the explanation? why is the different output is coming ? since i was able to understood that first print() that will print variable char x character value, but second print() printing 88 an ASCII- representation of value in char x.if i simplify the ternary operators expression in second print() like this
if(false){
System.out.print(i);
}else{
System.out.print(x);
}
then output is coming XX,quite strange, can anybody lighten this problem?
It would be great help for me to understand ternary operators.
Thanks in advance!
The reason for this behavior is that a ternary has one result type that the compiler has to choose in advance, and the flavor of print called in response.
In the true ? x : 0 case, 0 is treated as a char value, and print(char) is invoked. In the second case, since i is an int, x is also implicitly cast to int (a widening cast) and print(int) is invoked, yielding numeric output. The opposite, casting i to char implicitly, is illegal, because it could lose precision.
The implications of statically resolving the type can be shown with this example - not with print, because there is a print(Object), but consider this:
void method(boolean b);
void method(Integer i);
...
method(cond? false:0);
No matter what cond is, there is an overload compatible with the parameter. However, the compiler needs to chose one overload, which is not possible at compile time. The compiler will autobox both and assign the expression as Object*, but there is no method(Object).
*actually, my compiler said "The method method(boolean) in the type Test is not applicable for the arguments (Object&Serializable&Comparable<?>)", but the point stands.
Whatever result you are seeing is in accordance with the rules specified in JLS 15.25 for determining the type of conditional operation. Here is the point that it mentions:
If the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:
If one of the operands is of type byte or Byte and the other is of type short or Short, then the type of the conditional expression is short.
If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression (§15.28) of type int whose value is representable in type T, then the type of the conditional expression is T.
If one of the operands is of type T, where T is Byte, Short, or Character, and the other operand is a constant expression (§15.28) of type int whose value is representable in the type U which is the result of applying unboxing conversion to T, then the type of the
conditional expression is U.
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.
In your question, the last point is followed. x is char type while i is of int type , so x is promoted to int . Hence the output for the second line is 88 which is the int conversion of char X.
To understand what is happening here, we need to track the types. Lets consider each print statement in turn, first:
System.out.print(true ? x : 0);
Here the types are boolean ? char : const int
The compiler will want the ternary statement to return a single type, it cannot return either a char or an int. It spots that 0 can be a converted to char constant, and so treats it as the expression as 'boolean ? char : char'. Where the last char has the value zero. Because the boolean was true, the first char is printed as an X.
System.out.print(false ? i : x);
Here the types are boolean ? int : char, the int is an int variable and can not be treated as a char so the choice for the compiler is to either error as the types are different, narrow the int or widen the char. Narrowing the int would loose precision which, and so the compiler would not do it behind our back (it would require an explicit cast). Widening does not loose precision so the char is converted to an int. The int value of 'X' is 88. Thus the value 88 is printed.
More details of this conversion process can be read here: Java Language Spec
In this line :
System.out.print(false ? i : x);
X is casted to int, that's way prints the decimal value of char X
When you use a ternary operator condition?valTrue:valFalse . This will return the same datatype in both the case (true or false) . in line System.out.print(condition? i : x); you are aspecting int if true and Char if false , but it will return only int in both the case . similarly in line System.out.print(condition? x : i); in bothe case it will return Char .
In the first expression one side is a char, and 0 is assignable to a char; hence the first ternary expression is a char. char zero = 0;
In the second there are int and char, and the char is widened: (int)'X' is 88.
Constants like in c ? "" : null hence do not make an Object but a String (here).
Your observation is correct. But the ternary operator deal with the same data type. and it consider the data type of true vale and convert the type of false value if required. In your case at first print statement the ternary operator will consider data type as Char as the data type of x and at second print statement the ternary operator will consider data type as int as the data type of i that's the reason you are getting ASCII- representation of value in char x.
System.out.print(true ? x : 0);
/*ternary operator(compiler) consider the resulting data
type as char as the true Value type is char.*/
System.out.print(false ? i : x);
/*ternary operator(compiler) consider the resulting data
type as int as the true Value type is int.*/
/* but if you replace the print statement with if else condition
then there is no point of ternary concept at all*/
In a ternary statement typed like this: (boolean ?if type:else type), only the if type and the else type matters for the determination of the final type. The compiler deals with the situation as follows:
If both the if type and else type are same, then the final type is also the same.
If the if type can be converted to else type without loss of precision, the final type is the same as the else type.
If the else type can be converted to if type without loss of precision, the final type is the same as the if type.
If both 2 and 3 are possible, the final type is the same as the type of the lower data range.
If none of the above qualify, the compiler throws a compilation error.*
Let us take these various versions:
System.out.println(true ? x : 0); //X
This becomes X because the if type is char, and the else type is any type that can represent 0. The constant 0 is valid in the char data range (0 to 65,535). It is possible to treat x and 0 as int, but the int data range is higher (-231 to 231-1).
As per point 4, compiler picks the type of lower range (char).
System.out.println(true ? x : i); //88
Here, the if type is a char, and the else type is an int. When you declare the variable as a non-final int, the compiler cannot be sure that the value will never change from 0 (even if your code doesn't change it anywhere!).
Hence only point 2 applies here. The if type char can be converted to else type int without any loss in precision (as int has higher range), but if the else type int is converted to the if type char, the lower range of char can cause loss of precision (the value of i could be outside the range of char).
Therefore the compiler picks int to avoid loss of precision, as per point 2.
Some other test cases (similar reasons as already explained above):
System.out.println(false ? i : x); //88 (as per point 3)
System.out.println(false ? 0 : x); //X (as per point 4)
However, in this code, the result is different:
final int i = 0;
System.out.println(true ? x : i); //X
System.out.println(false ? i : x); //X
Can you tell why?
* Note: All types can ultimately convert to Object with no loss, including primitive types which can be auto-boxed (on recent Java versions). If the method is overloaded to accept Object type, you may never experience this case as the compiler converts the final type to Object.
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.
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.