Related
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.
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...
Why the output of following code is 9.0 and not 9 ? If ternary operator is nothing but short form of if-else branch then why java compiler is promoting int to double ?
public class Ternary
{
public static void main(String args[])
{
int a = 5;
System.out.println("Value is - " + ((a < 5) ? 9.9 : 9));
}
}
If ternary operator is nothing but short form of if-else branch then why java compiler is promoting int to double ?
A conditional expression has a single type, which both the second and third operands are converted to as necessary. The JLS gives the rules determining the expression type, which are slightly complicated due to auto-unboxing.
The conditional operator is sort of just shorthand for an if/else construct, but not the sort of shorthand I think you expected. So your code is equivalent to this:
double value;
if (a < 5) {
value = 9.9;
} else {
value = 9;
}
System.out.println("Value is - " + value);
It's not short for:
if (a < 5) {
System.out.println("Value is - " + 9.9);
} else {
System.out.println("Value is - " + 9);
}
For more details, see section 15.25 of the Java Language Specification.
Because the type of the conditional operator(Yes, it's conditional operator and not ternary operator) in this case will be the promoted type of the 3rd operand, since 2nd and 3rd operand are not of same type.
This is clearly listed in JLS Secion - 15.25: -
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.
See the last point, that is of use here. So, in this case, as a rule of binary numeric promotion - See JLS Section 5.6.2: -
If either operand is of type double, the other is converted to double.
Because the type of the expression as a whole is double, because one of the operands to the operator is a double. The type of the expression containing the ternary is dictated by the operands, which must be of the same type. In the case of your expression, the 9 is coerced to a double to make it the same type as the 9.9.
Actually the ternary operator is not strictly speaking a short form of if/else as it does perform type conversion if required. In particular, in your case, JLS 15.25 requires that:
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.
If you follow the link to §5.6.2:
If either operand is of type double, the other is converted to double.
Java needs to know the type of the result at compile time. So as this ternary operator can result an int or a double, the compiler chooses the double as the result type.
I have been told to never use == for strings but for everything else because .equals would compare the values rather than the instances of the object. (Which I understand the difference of).
According to some sites, == compares memory locations?
What I don't understand is if you're comparing an integer with another, why would it compare memory locations, or is this just for strings?
If you're comparing int 3 to int 4 obviously it wouldn't be in the same memory location, but then if you're comparing int 4 to int 4, does that mean all integers with the value of 4 is stored in the same memory location?
According to some sites, == compares memory locations?
The expression a == b compares the content of a and b, regardless of their types.
What I don't understand is if you're comparing an integer with another, why would it compare memory locations, or is this just for strings?
In case a and b are references, the == operator will compare "memory locations" since that is what the variables contain.
In case a and b are of primitive types, such as int or double the variables will contain actual values, consequently these values (and not their locations) will be compared.
(Note that a variable can never contain an object such as a String, it can at most point at an object.)
Does that mean all integers with the value of 4 is stored in the same memory location?
No. As explained above, ints are compared "directly". When it comes to Integer the story is slightly different. First of all new guarantees that you get hold of a fresh reference, that is
Object i = new Integer(5);
Object j = new Integer(5);
... i == j ...
will always yield false.
If you go through auto-boxing however:
Object i = (Integer) 5;
Object j = (Integer) 5;
... i == j ...
you'll get true due to the fact that auto-boxing goes through a cache for values in the range -128-127. (See for instance this question: Compare two Integer: why is == true?)
== compares the values of the oparands whether it is primitive or reference type.
If the operands are primitive the values of the operands will be compared.
Operands which are references contains values i.e. address to access the object they are referring to. String are not primitive data type, they are considered as objects in java, When you are comparing two references of type string, the result will be true only when the values of the operands i.e. address of the String objects are equal ( which means they refer to the same String object).
Simply put: the thing is that int is a primitive type whereas String is an object. The values of primitive types can be compared with == since the variable points to the value itself rather than the reference to the value.
int's are primitive types in java and as such they don't represent an object "reference" but the value directly.
== compares reference types. int is a primitive type.
so:
int x = 3;
int y = 3;
x==y is true
but using the Integer reference type
Integer x = new Integer(3);
Integer y = new Integer(3);
x == y is false
The == operator compares the references of objects in memory and Strings are objects - primitives aren't objects so as long as they are of the same type, then == will work. As you say, if they are the object variants of primitives (e.g. Integer for int) then java (>5) autoboxes in order to do the compare.
From the Java Specification 15.21 Equality Operators:
15.21 Equality Operators
The equality operators are syntactically left-associative (they group
left-to-right), but this fact is essentially never useful; for
example, a==b==c parses as (a==b)==c. The result type of a==b is
always boolean, and c must therefore be of type boolean or a
compile-time error occurs. Thus, a==b==c does not test to see whether
a, b, and c are all equal.
EqualityExpression:
RelationalExpression
EqualityExpression == RelationalExpression
EqualityExpression != RelationalExpression The == (equal to) and the!= (not equal to) operators are analogous to the relational
operators except for their lower precedence. Thus, a
In all cases, a!=b produces the same result as !(a==b). The equality
operators are commutative if the operand expressions have no side
effects.
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.
Floating-point equality testing is performed in accordance with the
rules of the IEEE 754 standard:
If either operand is NaN, then the result of == is false but the
result of != is true. Indeed, the test x!=x is true if and only if the
value of x is NaN. (The methods Float.isNaN and Double.isNaN may also
be used to test whether a value is NaN.) Positive zero and negative
zero are considered equal. Therefore, -0.0==0.0 is true, for example.
Otherwise, two distinct floating-point values are considered unequal
by the equality operators. In particular, there is one value
representing positive infinity and one value representing negative
infinity; each compares equal only to itself, and each compares
unequal to all other values. Subject to these considerations for
floating-point numbers, the following rules then hold for integer
operands or for floating-point operands other than NaN: The value
produced by the == operator is true if the value of the left-hand
operand is equal to the value of the right-hand operand; otherwise,
the result is false. The value produced by the != operator is true if
the value of the left-hand operand is not equal to the value of the
right-hand operand; otherwise, the result is false.
15.21.2 Boolean Equality Operators == and !=
If the operands of an equality operator are both of type boolean, or
if one operand is of type boolean and the other is of type Boolean,
then the operation is boolean equality. The boolean equality operators
are associative. If one of the operands is of type Boolean it is
subjected to unboxing conversion (§5.1.8).
The result of == is true if the operands (after any required unboxing
conversion) are both true or both false; otherwise, the result is
false.
The result of != is false if the operands are both true or both false;
otherwise, the result is true. Thus != behaves the same as ^
(§15.22.2) when applied to boolean operands.
15.21.3 Reference Equality Operators == and !=
If the operands of an equality operator are both of either reference
type or the null type, then the operation is object equality. A
compile-time error occurs if it is impossible to convert the type of
either operand to the type of the other by a casting conversion
(§5.5). The run-time values of the two operands would necessarily be
unequal.
At run time, the result of == is true if the operand values are both
null or both refer to the same object or array; otherwise, the result
is false.
The result of != is false if the operand values are both null or both
refer to the same object or array; otherwise, the result is true.
While == may be used to compare references of type String, such an
equality test determines whether or not the two operands refer to the
same String object. The result is false if the operands are distinct
String objects, even if they contain the same sequence of characters.
The contents of two strings s and t can be tested for equality by the
method invocation s.equals(t). See also §3.10.5.
The == operator compares ints by value and objects by address. Thus, == is correct for ints but (usually) not for Strings.
Note that if you know that both Strings have been returned by the String.intern method, == works correctly, as intern is guaranteed to return the same address for identical strings.
Objects are equality by value, but the value that objects have is a reference to the memory location. Primitives (i.e. int, boolean, char, double) do not use references, but store their value. So when == is used, it compares the value of the two. In the case of objects it's a reference; however, in the case of primitives it is the value it stores.
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.