Why Does the Ternary Operator Unexpectedly Cast Integers

Why does the ternary operator unexpectedly cast integers?

You need to read section 15.25 of the Java Language Specification.

In particular:

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 binary numeric promotion is applied, which starts with:

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.

That's exactly what happens here - the parameter types are converted to int and double respectively, the second operand (the third in the original expression) is then of type double, so the overall result type is double.

Java ternary operator is automatically wide casting an int to double during string concatenation

You need to pick a definite return type for your ternary operation. It has to both be something to which the 2nd and 3rd operands can be converted, and appropriate for how the ternary result will be used.

As a simple strategy, look at how you plan to use the result, and pick the type for that use. Force one or both operands to that type.

In the case in the question, the result is going to be concatenated with a String. Using "1" instead of 1 forces the ternary result type to String. key can be converted to String, so you only need to change one operand.

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;
}

Why does C# ternary operator require a cast that equivalent if() statement does not?

Because in the second case, we need to anaylze what the type of the conditional expression is first. The two subexpressions are byte or int (a literal in this case), and so the overall type of this expression is int. And it's not a literal at this point, it's a general expression.

We then try to assign this int to _bytes[3] which can only accept a byte and we get an error.

In the first case, we analyze two separate expressions and consider conversions for both separately. There, in the else, we have an assignment of a literal int to a byte. That's a special implicit conversion that's only allowed for literal int1; The conditional expression isn't a literal.

My preferred fix is this:

_bytes[ 3 ] = ( _hexColourString.Length >= 8 ) ?
byte.Parse( _hexColourString.Substring( start + 6, 2 ),NumberStyles.AllowHexSpecifier)
: (byte)0x00;

(newlines for readability only)

This now makes the type of the conditional byte rather than int.


1This conversion isn't considered when analysing the type of the conditional since we don't need it - we're always allowed an implicit conversion from byte to int so int is the best type for the conditional expression.

Java - ternary operator weird behaviour

The return type of the ternary conditional operator must be such that both the 2nd and 3rd operands can be assigned to it.

Therefore, in your second case, the return type of the operator is Object (since both d.intValue() and "not whole" must be assignable to it) while in the third case it is Double (since both d.intValue() and d must be assignable to it).

Printing an Object whose runtime type is Integer gives you 5 while printing a Double gives you 5.0.

Why does the ternary operator unexpectedly cast integers?

You need to read section 15.25 of the Java Language Specification.

In particular:

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 binary numeric promotion is applied, which starts with:

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.

That's exactly what happens here - the parameter types are converted to int and double respectively, the second operand (the third in the original expression) is then of type double, so the overall result type is 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

Weird behaviour of ternary conditional with boxed types in Java

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.

Unexpected behaviour in ternary operator

Result of ternary (conditional) operator is always of single type - one/both of the options is casted to common type:

var result = condition ? decimalValue : intValue;

Type of result must be known statically at compile time. Since there is cast from int to decimal than decimal type is selected as type of whole ? : operator.

So you whole function can be written as (showing automatic casts):

public object TurnaryGet
{
get
{
/*decimal*/ var result = condition ? decimalValue : (decimal)intValue;
return (object)result;
}
}


Related Topics



Leave a reply



Submit