Java Conditional Operator : Result Type

Java conditional operator ?: result type

The difference is static typing of the expressions at compile time:

Summary

E1: `(false ? 1.0f : null)`
- arg 2 '1.0f' : type float,
- arg 3 'null' : type null
- therefore operator ?: : type Float (see explanation below)
- therefore autobox arg2
- therefore autobox arg3

E2: `(false ? 1.0f : (false ? 1.0f : null))`
- arg 2 '1.0f' : type float
- arg 3 '(false ? 1.0f : null)' : type Float (this expr is same as E1)
- therefore, outer operator ?: : type float (see explanation below)
- therefore un-autobox arg3

Detailed Explanation:

Here's my understand from reading through the spec and working backwards from the result you got. It comes down to the type of the third operand of the f2 inner conditional is null type while the type of the third operand of the f2 outer conditional is deemed to be Float.

Note: Its important to remember that the determination of type and the insertion of boxing/unboxing code is done at compile-time. Actual execution of boxing/unboxing code is done at run-time.

Float f1 = (false ? 1.0f : null);
Float f2 = (false ? 1.0f : (false ? 1.0f : null));

The f1 conditional and the f2 inner conditional: (false ? 1.0f : null)

The f1 conditional and the f2 inner conditional are identical: (false ? 1.0f : null). The operand types in the f1 conditional and the f2 inner conditional are:

type of second operand = float
type of third operand = null type (§4.1)

Most of the rules in §15.25 are passed up and this final evaluation is indeed applied:

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).

S1 = float
S2 = null type
T1 = Float
T2 = null type
type of the f1 and f2 inner conditional expressions = Float

Since for f1, the assignment is to a Float reference variable, the result of the expression (null) is successfully assigned.

For f2 outer conditional: (false ? 1.0f : [f2 inner conditional])

For the f2 outer conditional, the types are:

type of second operand = float
type of third operand = Float

Note the difference in operand types compared to the f1/f2 inner conditionals that reference the null literal directly (§4.1). Because of this difference of having 2 numeric-convertible types, this rule from §15.12.2.7 applies:

  • 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 of the unboxing conversion performed on the result of the f2 inner conditional (null), a NullPointerException is raised.

Unexpected type resulting from the ternary operator

As the other answers have stated, this behavior is because both possible results of a ternary expression must have the same type.

Therefore, all you have to do to make your ternary version work the same way as convert1() is to cast the int to an Object:

static Object convert2(double d) {
return ((d % 1) == 0) ? ((Object) (int) (d)) : d;
}

java conditional operator and different types

Because to do anything else wouldn't make any sense. The ternary conditional operator must return some value of some specific type -- all expressions must result in a specific type at compile time. Further, note that overload resolution happens at compile time, as well. The behavior you are trying to invoke here (late binding) doesn't exist in this form in Java.

The type of the expression must be compatible with the true and false subexpressions. In this case, the nearest common ancestor class is Object, and you don't have a setValue(Object) overload.

This is the simplest way to rewrite what you have in a working way:

if (condition) {
item.setValue(str);
} else {
item.setValue(dbl);
}

You could also provide a setValue(Object) overload that inspects the type of object passed and delegates to the appropriate setValue() overload, throwing an exception if the type is not acceptable.

Evaluation of an expression in Java that contains conditional operator

For both primitive operands (int, double) and wrapper class operands (Integer, Double) binary numeric promotion will be applied.

bnp(int,double)
bnp(Integer,Double)

JLS-15.25

It means, first, for the second scenario, an unboxing conversion happens.

Integer -> int
Double -> double

Then, for both, a widening primitive conversion takes place resulting in a double

double
int -> double
double = expression ? double : double

JLS-5.6.2

which may be converted further based on the variable you assign the result to.

Why is the result of conditional operator opposite of expected?

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.

Java Ternary Operations - Accepting Integer

This happens because if we use wrapped primitive values like in the question (Integer, Float, Double etc.) in a ternary operator, both values will be unboxed and coerced to a common type.

This may result in unexpected results as well.

In the first example, new Integer(12) would be converted to a double as it is the common type. Hence there are no compilation issue.

But in the second example both are Integer which is not compatible with the left hand side type Double.

Example taken from Sonar rule description S2154:

Integer i = 123456789;
Float f = 1.0f;
// Assume condition = true
Number n = (condition) ? i : f; // i is coerced to float. n = 1.23456792E8

To fix this, we need to do explicit casting:

Integer i = 123456789;
Float f = 1.0f;
// Assume condition = true
Number n = condition ? (Number) i : f; // n = 123456789

Additional suggestion: Do not use new Double() or new Integer() etc. These constructors are deprecated. Instead use valueOf method

Nullpointer exception with conditional operator, (ternary operator) but not with if else

The behavior you encountered results from the rules of determining the type of the ternary conditional expression.

In your case, the type of the expression

val == null ? val : val.intValue();

is int.

This is specified by JLS 15.25. :

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.

Your second operand is Integer and your third operand is int, therefore the type of the expression is int.

Therefore, when val == null, val is un-boxed (i.e. val.intValue() is called for a null value) and a NullPointerException is thrown.

In your if-else expression val is not un-boxed when its value is null (since you assign it to an Object variable, so there's no NullPointerException.

That said, since you are assigning an Integer variable to an Object variable, your conditions in either of the snippets are pointless (since assigning an int to an Object variable simply boxes the int back to Integer).

You can simply assign

Object res = val;

and get the same end result without the exception.

Java ternary operator datatype conversion based on the first value?

The type of

(a < 5) ? "asd" : 9

is

Object&Serializable&Comparable<?>

You can see this in the compiler error later in the question. The int 9 is boxed to Integer and then the common type between that and String is found. So you are actually calling the println(Object) overload not println(String) or println(int). println(Object) calls toString on its argument.

If you try to apply + to a Object&Serializable&Comparable<?> then neither string concatenation or arithmetic is applicable, hence the compiler error.

Java Ternary operator outputs different result than if else statement

In your if/else statement, you're calling PrintStream.println(int) or PrintStream.println(double) depending on which branch you're taking.

With the conditional operator ?: you're always calling PrintStream.println(double), because that's the type of the ?: expression. When the second and third operands of the ?: operator have different types, the compiler picks the overall type of the expression according to the rules of JLS 15.25 and performs appropriate conversions where necessary.

In this case, the overall type is double, so it's as if you're writing:

double tmp = ((int) number == (double)number) ? (int)number : number;
System.out.println(tmp);


Related Topics



Leave a reply



Submit