Reputation: 28639
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
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
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
, orint
:
- A narrowing primitive conversion may be used if the type of the variable is
byte
,short
, orchar
, 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
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
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