Matt Clark
Matt Clark

Reputation: 28639

Type mismatch: cannot convert from int to byte using ternary operator

Working on a project utilizing byte array manipulation, I have attempted to write something like this:

boolean enabled = getEnabled();

byte enabledByte = enabled ? 0x01 : 0x00; // compile error

The issue that I am facing, is that the above will not compile.

Type mismatch: cannot convert from int to byte

However if I expand this to the following, it works without issue:

boolean enabled = getEnabled();

byte enabledByte;
if (enabled) {

    enabledByte = 0x01;
} else {

    enabledByte = 0x00;
}

Leading me to the question

Why can I not use byte assignments in short if statements?

Upvotes: 3

Views: 1394

Answers (4)

Andy Turner
Andy Turner

Reputation: 140544

0x00 and 0x01 are int literals: they are expressions with int type. An int can't usually be assigned to a byte, but it can if the expression is compile-time constant, provided the value is in the range of byte.

With the conditional, the assigned value is compile-time constant:

if (enabled) {
  enabledByte = 0x01;
} else {
  enabledByte = 0x00;
}

These assignments are completely separate from the compiler's point of view. There is one statement which assigns the constant value 1, and one statement which assigns the constant value 0.

For each statement, the compiler can guarantee that fits into the range of byte, so it automatically narrows the assigned value to byte.

However, the assignment with the conditional operator:

enabledByte = enabled ? 0x01 : 0x00;

The rhs expression is of type int, but isn't compile time constant, because enabled isn't known at compile time. It doesn't matter that the two possible values are compile-time constant, and both would fit into a byte: the non-constant first operand makes it non-constant. As such, the result of the conditional operator expression cannot be automatically narrowed.

The most efficient solution to this is to cast the second and third operands to byte:

enabled ? (byte) 0x01 : (byte) 0x00

This is better than

(byte) (enabled ? 0x01 : 0x00)

Because the latter will cast at runtime, each time the expression is evaluated, whereas the former does not: the operands are already bytes at compile-time, so no cast is necessary.

Upvotes: 2

M A
M A

Reputation: 72884

The rule governing the narrowing conversion from int to byte is specified in the language specification, section on assignment contexts (emphasis on constant expression):

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.

...

If the type of the expression cannot be converted to the type of the variable by a conversion permitted in an assignment context, then a compile-time error occurs.

The ternary conditional expression enabled ? 0x01 : 0x00 is not a constant expression, unlike the integer literals. Therefore, it cannot be implicitly converted using narrowing conversion.

Note that you can solve the error by casting one of the operands of the conditional expression:

byte enabledByte = enabled ? (byte) 0x01 : 0x00;

Upvotes: 0

Elliott Frisch
Elliott Frisch

Reputation: 201537

You need at least one cast to flip the ternary expression to a byte, it's defaulting to an int because of the ternary.

byte enabledByte = enabled ? (byte) 0x01 : 0x00; 

It works the same way if you use decimal literals

byte enabledByte = enabled ? (byte) 1 : 0; 

See also JLS-15.25.2 - Numeric Conditional Expressions which says (in part)

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.

Upvotes: 0

SLaks
SLaks

Reputation: 888223

Number literals are naturally of type int, not byte.

The language has a special implicit conversion from numeric literals (< 128) to byte, so your second example doesn't need a cast.

However, wrapping the literals in a conditional operator creates an expression of type int that isn't a literal, so you need the cast.

Upvotes: 2

Related Questions