I wrote following code in Eclipse:
byte b = 10;
/* some other operations */
b = ~b;
Eclipse wanted a cast to byte in the line of the bitwise complement. It said: "Type mismatch: cannot convert from int to byte". I also tried this with other bitwise operations and on other integral types. It was with short and char the same. Only long and integer could use bitwise operations.
Is there a reason for this?
Unary (such as ~
) and binary operators in Java subject their operands to "unary numeric promotion" (JLS, Section 5.6.1) and "binary numeric promotion" (JLS, Section 5.6.2), respectively, fancy terms for "promote things to at least int
first".
Specifically, for unary numeric promotion, quoting the JLS section linked above:
Some operators apply unary numeric promotion to a single operand, which must produce a value of a numeric type:
and
... if the operand is of compile-time type byte, short, or char, it is promoted to a value of type int by a widening primitive conversion (§5.1.2).
(Binary numeric promotion is similar, operating on both operands.)
So, even if b
is a byte
, ~b
is an int
, because b
's value was promoted to an int
first.
The solution: cast it back to a byte
:
b = (byte) (~b);
Why, Java?
That leaves the question, why? It seems for the operators I can find, the JVM bytecode instructions for operating on byte
s, short
s, and char
s simply do not exist. For example, the unary bitwise complement operator you're using (~) is implemented as an "XOR" operation with -1
(all bits set). From that link:
tempSpock &= ~mask;
becomes
25 iload_2 // Push local variable 2 (mask).
26 iconst_m1 // Push -1.
27 ixor // Bitwise EXCLUSIVE-OR top two ints: ~mask
However, I can only find instructions for XOR (and for other unary and binary operators too) for int
s and long
s (float
and double
version exist for other operators where appropriate).
So, Java must perform those promotions because there are no bytecode instructions for performing those operations on byte
s, short
s, or char
s.
Why not, JVM?
That brings up another question: Why doesn't the JVM support such bytecode instructions? The answer appears to be, "Because there would be too many to encode them all in a one-byte instruction set." According to the JVM Specification, Section 2.11.1,
Given the Java Virtual Machine's one-byte opcode size, encoding types into opcodes places pressure on the design of its instruction set. If each typed instruction supported all of the Java Virtual Machine's run-time data types, there would be more instructions than could be represented in a byte. Instead, the instruction set of the Java Virtual Machine provides a reduced level of type support for certain operations. In other words, the instruction set is intentionally not orthogonal. Separate instructions can be used to convert between unsupported and supported data types as necessary.
(emphasis mine)
In conclusion, the JVM's one-bytecode instruction set precludes the bytecode instructions for most operations on byte
s, char
s, and short
s, necessitating unary numeric promotion and binary numeric promotion.
Yes, types smaller than int
undergo promotion when used as operands to most operators. They are effectively cast to int
first. Therefore, in your code above, the type of the result is int
. See this section of the JLS for the full details.
As for why Java does this, I'm not sure. But one plausible reason is that C does this, and maintaining familiar semantics was probably a language design objective.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With