Java ternary operator not working? - java

Let's assume we have an StringBuilder and an double. Now want to append the double. If the double can be represent as Integer (for example 3.0, 5.0 etc.) i want to add it as Integer, otherwise as double.
The first method to realize this is:
StringBuilder sb = new StringBuilder();
double d = 3.5;
if (d % 1 == 0) sb.append((int) d);
else sb.append(d);
System.out.println(sb.toString());
This works still good, when d is 3.0 3 will be append, if d is 3.5 3.5 will be append.
Now i want to do this shortly with the ternary operator:
StringBuilder sb = new StringBuilder();
double d = 3.5;
sb.append(d % 1 == 0 ? (int) d : d);
System.out.println(sb.toString());
So now i have an issue, every time, if double is 3.0 or 3.5 it will be added as double value! Only when i theoretically cast on true AND false it works... but every time and that is not what I want. What is here the problem? Why does the ternary operator not work?

This behavior is documented in the JLS - 15.25. Conditional Operator ? : :
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
When you write
(a > b) ? 'a' : 65
the second type is converted to a char.
Go through the JLS, it explains the behavior (same approach) in other cases.

The reason for this behaviour is that an expression with a ternary operator has a well-defined type. The JLS describes in some detail how this type is evaluated, but in rough terms, it's the least upper bound of the type of the expression before the colon and the type of the expression after the colon.
For example, if b is boolean, i is int and d is double, then the type of b ? i : d is double, because double is the least upper bound of int and double. When you call append( b ? i : d ) on your StringBuilder, you get the version of append with the double parameter. The same thing happens in your case, with d % 1 == 0 ? (int) d : d.

When using the ternary operator on primitive numbers, the 2nd and 3rd operand are subject to binary numeric promotion. In your case, the int is cast to a double. This is specified in the JLS #15.25.

Method StringBuilder.append() has many overloads for different type of parameters. Which method overload is used is a compile-time decision. The result of ternary operator has just a single type, either int or double - double in this case.
In the case of if statement, compiler uses the proper append() method overload depending on the branch.

I think the compiler regards sb.append(d % 1 == 0 ? (int) d : d) as sb.append(double) ,otherwise sb.append((int) d) as sb.append(int) .

Numeric conditional expressions are standalone expressions (§15.2).
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.
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 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 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.
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.

Related

Why is there no need to explicitly cast in case of integers?

byte a=10;
byte b=20;
b=a+b;
In this case, I need to explicitly convert a+b to byte like this :
b=(byte)(a+b);
It's the same with short :
short x=23;
short y=24;
Otherwise it gives an error.
But in case of integers, it's not required to convert explicitly :
int p=7788;
int q=7668;
p=p+q;
This will work just fine.
Why is that?
We don't need to explicitly cast even in the case of long as well.
If you look at the JLS 4.2.2 Integer Operations, it states that the result of a numerical operation between two integral operands is an int or a long. Since there's no implicit cast from an int to byte or a short, you need an explicit cast.
If you refer to JLS Sec 15.18.2, which deals with addition of numeric types, it says:
Binary numeric promotion is performed on the operands (§5.6.2).
...
The type of an additive expression on numeric operands is the promoted type of its operands.
JLS Sec 5.6.2 describes binary numeric promotion:
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.
So, in the case of int and long (where both operands are of that type), binary numeric promotion is a no-op: the operands remain int and long respectively, and the result of the addition is int and long respectively, meaning the result can be assigned to variables of that type.
In the case of byte and short, binary numeric promotion causes both of those to be widened to int to perform the addition, and the result of the addition is int; you have to explicitly cast back again to the narrower type, because not all int values fit into a byte or short.
There are 2 exceptions to this requirement to do an explicit narrowing cast.
Firstly, compound assignments: this would have worked:
b += a;
because, as stated in JLS Sec 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.
In other words: the compiler inserts the cast for you:
b = (byte) ((b) + (a));
Secondly, if the operands have constant values, and the result of the addition is known to fit into the range of the narrower type, and you are doing the assignment at the variable declaration.
For example:
final byte a=10; // final is necessary for a and b to be constant expressions.
final byte b=20;
byte c = a + b;
This requires no cast.
With addition in java, java will promote the smaller data type to the larger one. And when the data type is smaller than int it will promote both the operant to int. Take a look at: Promotion in Java?

Why is the result of conditional operator opposite of expected? [duplicate]

This question already has answers here:
Unexpected type resulting from the ternary operator
(4 answers)
Closed 8 years ago.
Object myObject = true ? new Integer(25) : new Double(25.0);
System.out.println(myObject);
Strangely, it outputs 25.0 instead of 25
Whats going on?
Your code returns the second operand (new Integer(25)) as you expected, but it converts it to a Double due to the following rules.
Here's what JLS 15.25 says :
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).
And the numeric promotion :
5.6.2. Binary Numeric Promotion
When an operator applies binary numeric promotion to a pair of operands, each of which must denote a value that is convertible to a numeric type, the following rules apply, in order:
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.
In your example, you have an Integer and a Double. They are unboxed to int and double and then the int is converted to a double.
There is some wierd autoboxing stuff going on - you can see it better if you use different numbers:
Object myObject = true ? new Integer(25) : new Double(22.0);
Now, myObject will still be assigned a Double(25.0), not the 22.0 you would expect if the conditional didn't work. Basically, because Java thinks you are doing some sort of calculation involving an int and a double it returns the result of the iif as a "double" primative and then autoboxes it back to a Double().
You could also get it to behave as expected by forcing it to treat the values as type Object():
Object myObject = true ? (Object) new Integer(25) : (Object) new Double(22.0);
After compilation
Object myObject = true ? new Integer(25) : new Double(25.0);
will be something as below
Object myObject = (double) new Integer(25);
So, it returns new Integer(25) as expected but it gets converted to double.
As Eran mentioned, as per JLS 5.6.2:
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 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.

Cannot set a short value using "(expr)?<val1>:<val2>" notation in Java

I'm encountering a strange issue which I do not quite understand, but it's not a show-stopper for my program. I am setting the value of a short variable based on the result of a conditional, and it works fine if I use a standard if/else but not if I use a shorthand conditional assignment.
Here's a simple example:
public class ShortTest {
public static void main(String args []) {
int i = 0;
short s = 0;
// This always works
if( i < 100 )
s = 0;
else
s = 1;
// This causes an error
s = (i < 100)?1:0;
}
}
Compiling this code results in the following:
ShortTest.java:13: error: possible loss of precision
s = (i < 100)?1:0;
^
required: short
found: int
1 error
But, if you add a cast to short to the 1, 0, or both, then it works fine. All three of these statements will work:
s = (i < 100)?(short)1:0;
s = (i < 100)?1:(short)0;
s = (i < 100)?(short)1:(short)0;
It's not a big deal because the if/else is fine, but this is driving me nuts. Is there a reason why these values can't be cast from int to short automatically when they're in a shorthand conditional? And why does casting just one of them suddenly make the whole statement valid? Any insights will be greatly appreciated!
1 and 0 are treat as literal int values here, and the result of this operation is an int, which cannot be narrowed by the compiler. You should add a cast explicitly:
s = (short) ((i < 100)? 1:0);
This is explained in Java Language Specification. Chapter 15. Expressions. 15.25. Conditional Operator ?.
The conditional operator has three operand expressions. ? appears between the first and second expressions, and : appears between the second and third expressions.
(...)
If both the second and the third operand expressions are numeric expressions, the conditional expression is a numeric conditional expression.
(...)
Numeric conditional expressions are standalone expressions (§15.2).
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.
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 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 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).
To add some details: As has been pointed out, integer literals always have type int (when there's no L suffix). A statement like this:
s = 1; // where s is short
therefore performs a narrowing conversion, from int to short. Normally this kind of conversion isn't allowed automatically. However, §5.2 of the JLS has some special rules that apply to constant expressions:
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.
This means that this is also legal:
s = 1 + 2;
But unfortunately not this:
s = (i < 100) ? 1 : 0;
since the expression on the right doesn't meet the definition of a constant expression. In theory, it would be possible for §5.2 to allow this case, by applying either to a constant expression or to a conditional expression where the second and third operands are both constant expressions (or conditional expressions that would be allowed, recursively). Whether it's worthwhile to suggest this as a language change, I can't tell.

Faulty behavior in Java in conditional expression

A simple expression:
Object val = true ? 1l : 0.5;
What type is val? Well, logically, val should be a Long object with value 1. But Java thinks that val is a Double with value 1.0.
It doesn't have to to anything with autoboxing as
Object val = true ? new Long(1) : new Double(0.5);
results with same behavior.
Just to clarify:
Object val = true ? "1" : 0.5;
results in the correct String.
Can anybody explain me why they have defined this like that? For me it seems to be rather really bad designed or actually a bug.
This is described in the Java Language Specification Section 15.25 (relevant parts in bold face):
The type of a conditional expression is determined as follows:
If the second and third operands have the same type [...]
If one of the second and third operands is of type boolean [...]
If one of the second and third operands is of the null 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 [...]
If one of the operands is of type T where T is byte, short, or char, [...]
If one of the operands is of type Byte [...]
If one of the operands is of type Short [...]
If one of the operands is of type; Character [...]
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).
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).
The "lub" mentioned in the last paragraph stands for least upper bound and refers to the most specific super type common for T1 and T2.
As for the case with Object val = true ? 1l : 0.5; I agree that it would be more precise if it applied rule 5 (on the boxed values). I guess the rules would become ambiguous (or even more complicated) when taking autoboxing into account. What type would for instance the expression b ? new Byte(0) : 0.5 have?
You can however force it to use rule 5 by doing
Object val = false ? (Number) 1L : .5;
Because the expression needs to be of a type.
If you do 1l + 0.5d, you end with 1.5d because the compiler changes automatically the types to the one which can allow all possible results.
In your case, the compiler sees ? and assigns the result of the expression the double using the same rule (you can write a long as a double, but not all doubles as longs).
That's not a design flaw:
The compiler has to determine the type of the entire expression true ? 1l : 0.5. It tries using a type where as few conversions as possible are required.
As 0.5 does not fit into a long (without loss of precision), the result of the expression can't be long - therefore, it has to be double and the long is simply converted (no loss of precision).
Note that you can't have an expression conditionally having different types - a tertiary expression has to evaulate to the same type regardless of the condition.
In the second snippet, Object is the only compatible type - the Double gets boxed and the String can be simply assigned.
When you write
Object val = true ? new Long(1) : new Double(0.5);
you need to consider that
(new Long(1) : new Double(0.5))
needs to have a value... The compiler needs to come up with a type that covers both possible values. For your example, it was double.
I cannot provide you with a good reason, but the type of the expression is given by the Java Language Specification: http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.25
In the JLS all the numeric types are treated as special cases.

Java auto boxing/unboxing wierdness [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicates:
Booleans, conditional operators and autoboxing
Java, Google Collections Library; problem with AbstractIterator?
The code below produces a NPE:
Integer test = null;
Integer test2 = true ? test : 0;
System.out.println(test2);
To correctly print out "null" without an exception requires this code:
Integer test = null;
Integer test2 = true ? test : (Integer)0;
System.out.println(test2);
It's obvious in the first example that "test" is being unboxed (converted to native int), but why? And why does changing the other expression in the ternary operator (as in the 2nd example) fix it? Can anyone provide some kind of narrative of exactly when, what, and why stuff in both of the examples gets boxed and unboxed?
From section 15.25 of the Java Language Specification:
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 type boolean and the type of the other is of type Boolean, then the type of the conditional expression is boolean.
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 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 it's following the final bullet, performing binary numeric promotion, which performs an unboxing conversion. So the type of the conditional operator expression is int, even though you're assigning it to an Integer. It's trying to perform the unboxing conversion on null, hence the exception.

Categories

Resources