Faulty behavior in Java in conditional expression - java

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.

Related

Java ternary operator not working?

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.

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.

Behaviour of shortcut assignment and normal assignment for float

I have a simple doubt
float k = 0;
k+=0.2;
k=k+0.2; // here compliation error
compliation error Type mismatch: cannot convert from double to float
My question is why not a complilation error at k+=0.2;
From the Java specification:
15.26.2. Compound Assignment Operators
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.
So the += operator has a built-in cast to the destination type.
In contrast, the clause for simple assignment says:
15.26.1. Simple Assignment Operator =
A compile-time error occurs if the type of the right-hand operand cannot be converted to the type of the variable by assignment conversion (§5.2).
Clause 5.2 allows widening conversions but does not allow narrowing conversions. So, in k+.2, the constant .2 has type double, and this causes the expression to have type double, and a double expression may not be directly assigned to a float object; it requires an explicit cast.
In Java by default decimal number consider as double.
Float numbers you need to append 'f' or 'F' at end of the number like this k=k+0.2f; or k=k+0.2F;
Java assumes literal decimal values are doubles. You need to explicitly put an "f" or "F" after the value to force it to be a float.
If Java allowed widening conversions from double to float, as well as float to double, then assignments like yours would compile fine, but expressions such as someFloat == someDouble would be ambiguous. To avoid such ambiguity, Java regards all primitive types in sequence such that each type may be converted to the next higher type, but not vice versa. Even though the percentage of implicit float-to-double conversions which represent programming mistakes far exceeds the percentage of implicit double-to-float conversions that would do so(*), the desire to arrange types into a strict sequence while avoiding having someFloat == someDouble translate as someFloat == (float)someDouble motivated type-casting rules which allow constructs which are more likely than not wrong to be done without typecasts, while requiring typecasts in cases where the behavior would be obvious without them.
In your cast, the compiler could be made not to squawk if you either specify that a constant is a float rather than a double, by adding an f suffix, or else avoid using float and simply use double. I would generally advise the latter approach. If you use float and later change your variables to type double, your maths will be compile fine but yield erroneous results unless you remove the suffixes from your constants (and also remove any explicit typecasts to float of things like function return values). A statement like double d=0.1f; will set d equal to precisely 13421773/134217728 (i.e. 0.100000001490116), a value substantially larger than was probably intended, without any compiler squawk whatsoever.

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.

NullPointerException with autoboxing in ternary expression

Run the following Java code:
boolean b = false;
Double d1 = 0d;
Double d2 = null;
Double d = b ? d1.doubleValue() : d2;
Why is there a NullPointerException?
The return type of the conditional expression b ? d1.doubleValue : d2 is double. A conditional expression must have a single return type. Following the rules for binary numeric promotion, d2 is autounboxed to a double, which causes a NullPointerException when d2 == null.
From the language spec, section §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: ...
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).
Because the two expressions around : must return the same type. This means Java tries to convert the expression d2 to double. This means the bytecode calls doubleValue() on d2 -> NPE.
You should generally avoid mixed type computation; compounding this with ?: conditional/ternary only makes it worse.
Here's a quote from Java Puzzlers, Puzzle 8: Dos Equis:
Mixed-type computation can be confusing. Nowhere is this more apparent than conditional expression. [...]
The rules for determining the result type of a conditional expression are too long and complex to reproduce in their entirety, but here are three key points.
If the second and third operands have the same type, that is the type of the conditional expression. In other words, you can avoid the whole mess by steering clear of mixed-type computation.
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 representible in type T, the type of the conditional expression is T.
Otherwise, binary numeric promotion is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.
Point 3 is applied here, and it resulted in unboxing. When you unbox null, naturally a NullPointerException is thrown.
Here's another example of mixed-type computation and ?: that may be surprising:
Number n = true ? Integer.valueOf(1) : Double.valueOf(2);
System.out.println(n); // "1.0"
System.out.println(n instanceof Integer); // "false"
System.out.println(n instanceof Double); // "true"
Mixed-type computation is the subject of at least 3 Java Puzzlers.
In closing, here's what Java Puzzlers prescribes:
4.1. Mixed-type computations are confusing
Prescription: Avoid mixed-type computations.
When using the ?: operator with numeric operands, use the same numeric type for both the second and third operands.
On prefering primitive types to boxed primitives
Here's a quote from Effective Java 2nd Edition, Item 49: Prefer primitive types to boxed primitives:
In summary, use primitives in preference to boxed primitive whenever you have the choice. Primitive types are simpler and faster. If you must use boxed primitives, be careful! Autoboxing reduces the verbosity, but not the danger, of using boxed primitives. When your program compares two boxed primitives with the == operator, it does an identity comparison, which is almost certainly not what you want. When your program does mixed-type computations involving boxed and unboxed primitives, it does unboxing, and when your program does unboxing, it can throw NullPointerException. Finally, when your program boxes primitive values, it can result in costly and unnecessary object creations.
There are places where you have no choice but to use boxed primitives, e.g. generics, but otherwise you should seriously consider if a decision to use boxed primitives is justified.
Related questions
What is the difference between an int and an Integer in Java/C#?
Why does autoboxing in Java allow me to have 3 possible values for a boolean?
Is it guaranteed that new Integer(i) == i in Java? (YES!!!)
When comparing two Integers in Java does auto-unboxing occur? (NO!!!)
Java noob: generics over objects only? (yes, unfortunately)
Why does int num = Integer.getInteger(“123”) throw NullPointerException?
Return same type for both condition like below and you will get result.
boolean b = false;
Double d1 = 0d;
Double d2 = null;
Double d = b ? d1 : (Double)d2;
System.out.println(d);

Categories

Resources