Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why in a Java switch over an Integer wrapper, does a 'char' case not compile, but compilation is OK when the switch is over Byte?

Does not compile:

void test(Integer x) {
      switch (x) {
          case 'a':
      }
}

Compiles OK:

void test(Byte x) {
      switch(x) {
          case 'a':
      }
}
like image 551
al gh Avatar asked Oct 04 '19 21:10

al gh


1 Answers

The reasons are rather complicated, but they are all in the details (fine print if you like) of the Java Language Specification.

First of, the JLS 14.11 says the following about switch statements:

"Every case constant associated with the switch statement must be assignment compatible with the type of the switch statement's Expression (§5.2)."

This means that 'a' needs to be assignable to Integer and Byte respectively.

But that doesn't sound right:

  • You would think that since 'a' should be assignable to an Integer because char -> int assignment is legal. (Any char value will fit into an int.)

  • You would think that since 'a' should NOT be assignable to an Byte because char -> byte assignment is NOT legal. (Most char values won't fit into a byte.)

In fact, neither of these is correct. To understand why, we need to read what JLS 5.2 actually about what is allowed in assignment contexts.

"Assignment contexts allow the use of one of the following:

  • an identity conversion (§5.1.1)
  • a widening primitive conversion (§5.1.2)
  • a widening reference conversion (§5.1.5)
  • a widening reference conversion followed by an unboxing conversion
  • a widening reference conversion followed by an unboxing conversion, then followed by a widening primitive conversion
  • a boxing conversion (§5.1.7)
  • a boxing conversion followed by a widening reference conversion
  • an unboxing conversion (§5.1.8)
  • an unboxing conversion followed by a widening primitive conversion."

To go from 'a' to Integer, we would need to1 widen the char value to an int then box the int to an Integer. But if you look at the combinations of conversions that are allowed, you cannot do a widening primitive conversion followed by a boxing conversion.

Therefore 'a' to Integer is not allowed. This explains the compilation error in the first case.

You would think that 'a' to Byte is disallowed because that would involve a primitive narrowing conversion ... which is not in the list at all. In fact, literals are a special case. JLS 5.2 goes on to say the following.

"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 variable is of type byte, short, or char, and the value of the constant expression is representable in the type of the variable.

  • A narrowing primitive conversion followed by a boxing conversion may be used if the variable is of type Byte, Short, or Character, and the value of the constant expression is representable in the type byte, short, or char respectively."

The second of these applies to 'a' to Byte, because:

  • a character literal is a constant expression, and
  • the value of 'a' is 97 decimal, which is within the range for byte (-128 to +127).

This explains why there is no compilation error in the second example.


1 - We can't box 'a' to a Character and then widen Character to Integer because Character is not a Java subtype of Integer. You can only use a widening reference conversion if the source type is a subtype of the target type.

like image 80
Stephen C Avatar answered Nov 08 '22 09:11

Stephen C