Can you please run the below and explain?
Object o = true ? new Integer(1) : new Double(2.0);
System.out.println(o);
I found that surprising as someone would expect 1 to be printed and not 1.0
It's not a surprise at all, although it might seem like one. The behaviour is specified in JLS §15.25 - Conditional Operator:
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.
[...]
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 value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
So the Integer and Double types are unboxed to their respective primitive counterparts - int and double, as a process of binary numeric promotion. And then the type of the conditional operator is the promoted type of int and double, which is double. Hence the result is 1.0. And of course the final result is then boxed back to Double.
Here is an article published in DZone yesterday talking about it:
Java auto unboxing gotcha
Funny enough, the example code looks similar...
Related
In Java when I print something like this
System.out.println(1.0f*1l));
I get an output of
1.0
or with
System.out.println(((byte)1)*'A');
I get an output of
65
In both cases, one of the types is bigger than the other. Longs are 64-bits while floats are 32-bits, and bytes are 8-bits while chars are 16-bits. Despite this, Java outputs the result in the smaller type. Wouldn't this be considered a loss of precision?
Java Language Specification provides a list of rules governing the type of result based on the types of operands.
In particular, section 4.2.4 says that
If at least one of the operands to a binary operator is of floating-point type, then the operation is a floating-point operation, even if the other is integral.
which explains why float "wins" against a long.
Integer arithmetic is explained in section 5.6.2. Specifically, it says that
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.
That is why in your second example the result, 65, is of type int.
Java widens type based on the range of possible values. This means float is considered wider thanlong.
Also integer operations which are not long are performed as int.
A byte * char is an int
You are asking about Binary Numeric Promotion (JLS 5.6.2) (emphasis mine):
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.
The expression 1.0f*1l is a float and long, so the long is converted to a float.
The expression ((byte)1)*'A' is a byte and a char, so they are both converted to int.
In addition to the other answers that spell out the rules in detail, I think it's also worth considering why it should be this way. Suppose you do
2.7F * 2L
The true mathematical answer to this multiplication is 5.4. Therefore if the answer were a long, it would have to be 5L (5 is the closest integer to 5.4). Because of this kind of thing it makes much more sense for the result of multiplying a floating-point value by an integer to be a floating point value. I think there would be many more questions about this on Stack Overflow if the following line of code printed 5.
System.out.println(2.7F * 2L);
In fact it prints 5.4.
It also makes sense that all operations between integer types (except long) are done with int. After all, a byte is usually thought of as a small packet of data, and a char is usually thought of as a character. From these perspectives multiplying byte or char values isn't really meaningful. It makes much more sense to keep the rules simple and just do the operations with int.
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);
This question already has answers here:
Why does the ternary operator unexpectedly cast integers?
(3 answers)
Closed 7 years ago.
I had this piece of code in my application (simplified version):
Object result;
if (check)
result = new Integer(1);
else
result = new Double(1.0);
System.out.println(result);
return result;
Then I decided to refactor the if-else statement to a ternary conditional expression so my code is more concise:
Object result = check ? new Integer(1) : new Double(1.0);
System.out.println(result);
return result;
It turned out that in case check is true the two versions print different results:
1
or:
1.0
Isn't the ternary conditional equivalent to the corresponding if-else?
The if/else and the conditional (ternary) expression are not quite equivalent. The result of the conditional expression must have a type.
You're observing the effects of numeric type promotion (or type coercion).
Here's an excerpt from the language spec (see here), from the section describing the return value of the conditional expression:
Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:
The final such case is (I've omitted the other cases here):
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.
Here's the additional spec related to binary numeric promotion:
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.
It's the first case (following cases omitted). double always wins.
So regardless of the order of the 2nd and 3rd operands in your conditional expression, the return type of the expression will be promoted to double.
Put simply, the ternary operator isn't different from other operators where numeric type promotion is concerned.
When you have something like System.out.println(1 + 1.0), you expect it to print 2.0, because the operands used in the output are subject to numeric type promotions.
It's exactly the same with the ternary operator: System.out.println(true ? 1 : 1.0) will print 1.0 after doing the same numeric type promotions it would have done if the expression was 1 + 1.0.
It works that way for a very simple reason: the type of the operator's result should be known at compile-time, while its actual result is determined at run-time.
Short answer
The first example explicitly typed as Object, which causes an upcast.
The second example is implicitly typed as Double, which causes numeric widening.
Long answer
In the example with Object, there is no conversion of values, just an upcast, and 1 is printed.
Object result;
if (1 == 1)
result = new Integer(1);
else
result = new Double(1.0);
If you instead declared using Double, it would be a widening and print 1.0.
Double result;
if (1 == 1)
result = new Integer(1);
else
result = new Double(1.0);
These are rather straightforward since there is an explicit type.
The ternary expression, however, has no explicit type, and the rules are non-trivial.
The type of a conditional expression is determined as follows:
If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.
If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.
If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type.
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 (§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. Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).
http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.25
The "promoted type" of numerics Integer and Double is Double.
In addition to #pb2q answer
you can verify it as
public class test {
public static void main(String[] args) {
Object result;
Boolean check = true;
if (check)
result = new Integer(1);
else
result = new Double(1.0);
System.out.println(result);
result = check ? new Integer(2) : new Double(1.0);
System.out.println(result);
}
}
it will print 2.0 instead of 2 because of numeric promotion
In addition to the existing answers, you can avoid the problem by specific casting to the type you want:
Object result = args.length < 100 ? (Object)2 : (Object)1.0;
Casting to Object boxes the integer as an Integer, and the double as a Double. The expressions on either side of the ":" are then both of type Object, so the compiler does not need to generate any further conversions.
Just trying to understand auto-boxing, which I do apart from one thing:
Short s = 250;
Long l = 250;
The assignment to Long l fails. This, I expect, is because you cannot widen then box (i.e. it tries to widen the int value 250 to a long and then box it which it cannot do).
However, the assignment to Short s works. What is going on to make this fine? My assumption was it is still doing boxing and some kind of conversion. But if it's a case of it knowing 250 fits into a short, why does it not know that 250 will fit into a long?
Normally, you cannot apply multiple (implicit) conversions in assignment (JLS §5.2 Assignment 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.
Long l=250; requires two conversions (widening primitive conversion followed by boxing conversion), that's why it doesn't compile.
Long l=250l; compiles because it requires a single boxing conversion.
But narrowing conversion of a constant expression is a special case, that's why Short s=250; compiles:
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.
Ideally, no auto narrowing should be allowed.
But since there are no byte/short literals, we can't write
byte b = 0b;
and it seems silly to
byte b = (byte)0;
so auto narrowing of constant integer is allowed so we can write
byte b = 0;
which is carried over to autoboxing case.
For long/Long, since there are long literals, this is less of a problem. Still, it should be allowed, since auto widening of signed integer is always safe.
I was wondering if java automatically turns a Integer into an int when comparing to an int? Or will the == try and compare references on primitives?
Is this always true or do I need to do i.intValue()==2?
Integer i = Integer.valueOf(2);
if (i==2){
//always?
}
Yes, when comparing int using == arguments will be unboxed if necessary.
Relevant section from the Java Language Specification:
15.21.1 Numerical Equality Operators == and !=
If the operands of an equality operator are both of numeric type, or one is of numeric type and the other is convertible (§5.1.8) to numeric type, binary numeric promotion is performed on the operands (§5.6.2). If the promoted type of the operands is int or long, then an integer equality test is performed; if the promoted type is float or double, then a floating-point equality test is performed.
Note that binary numeric promotion performs value set conversion (§5.1.13) and unboxing conversion (§5.1.8). Comparison is carried out accurately on floating-point values, no matter what value sets their representing values were drawn from.
Same applies for <, <=, >, >= etc, as well as +, -, * and so on.
So,
System.out.println(Integer.valueOf(17) == 17);
prints true :-)
but you can compare two equal strings with == and sometimes get true or fals depending on how the strings were pooled...
Right, and there is actually a similar situation for Integers as well.
When boxing (transforming int to Integer) the compiler uses a cache for small values (-128 - 127) and reuses the same objects for the same values, so perhaps a bit surprising, we have the following:
System.out.println(Integer.valueOf(100) == Integer.valueOf(100)); // prints true
System.out.println(Integer.valueOf(200) == Integer.valueOf(200)); // prints false
Yes, it will unbox. This is covered in section 15.21.1 of the JLS (the numeric == operator):
If the operands of an equality operator are both of numeric type, or one is of numeric type and the other is convertible (§5.1.8) to numeric type, binary numeric promotion is performed on the operands (§5.6.2). If the promoted type of the operands is int or long, then an integer equality test is performed; if the promoted type is float or double, then a floating-point equality test is performed.
Note that binary numeric promotion performs value set conversion (§5.1.13) and unboxing conversion (§5.1.8).
(I've linkified section 5.1.8 as that's what talks about the conversion from Integer to int being available.)
This is possible.
That Java-feature is called Autoboxing.
yes, it's automatically converted.
you can also do
Integer i = 2;
Yes this works because auto (un)boxing.
It will compare primitives - the Integer will be unboxed. But as a rule of thumb: avoid that. Always prefer primitives, and be careful when comparing objects with ==
Apart from seeing this in the JLS, here's how you can verify that:
Instead of Integer.valueOf(2), which uses a cache, use new Integer(2). This is guaranteed to be a different instance than the one that will be obtained if 2 if boxed (the boxing happens with Integer.valueOf(..)). In this case, the condition is still true, which means that it's not references that are compared.