¿Por qué en un conmutador Java a través de un contenedor Integer, no se compila un caso 'char', pero la compilación está bien cuando el conmutador supera el byte?

18

No compila:

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

Compila bien:

void test(Byte x) {
      switch(x) {
          case 'a':
      }
}
Ali GH
fuente
1
El entero es de 4 bytes mientras que el de char es de 2 bytes. Entonces, en el primer caso, no importa qué carácter escriba, es más pequeño que un entero. Sin embargo, en el segundo caso, el carácter que escribió podría ser más grande que el byte máximo, haciendo que ese caso nunca se ejecute.
Jaroslaw Pawlak
Esa explicación es incorrecta. De hecho, en el segundo ejemplo, el código en el 'a'caso se ejecutará en el caso que xsea ​​el byte 97. (Pruébelo si no me cree). Para la explicación real, vea mi respuesta.
Stephen C

Respuestas:

19

Los motivos son bastante complicados, pero todos están en los detalles ( letra pequeña si lo desea) de la Especificación del lenguaje Java.

Primero, el JLS 14.11 dice lo siguiente sobre las switchdeclaraciones:

"Cada constante de caso asociada con la declaración del interruptor debe ser compatible con el tipo de expresión de la declaración del interruptor ( §5.2 )".

Esto significa que 'a'debe ser asignable a Integery Byte respectivamente.

Pero eso no suena bien:

  • Se podría pensar que ya 'a' debería ser asignable a un Integerporque char-> la int asignación es legal. (Cualquier charvalor encajará en un int.)

  • Pensaría que ya 'a' NO debería poder asignarse a un Byteporque char-> la byte asignación NO es legal. (La mayoría de los charvalores no caben en un byte).

De hecho, ninguno de estos es correcto. Para entender por qué, necesitamos leer lo que JLS 5.2 realmente trata sobre lo que está permitido en contextos de asignación.

"Los contextos de asignación permiten el uso de uno de los siguientes :

  • una conversión de identidad (§5.1.1)
  • una conversión primitiva cada vez mayor (§5.1.2)
  • Una conversión de referencia cada vez mayor (§5.1.5)
  • una conversión de referencia cada vez mayor seguida de una conversión de unboxing
  • una conversión de referencia de ampliación seguida de una conversión de unboxing, seguida de una conversión primitiva de ampliación
  • una conversión de boxeo (§5.1.7)
  • una conversión de boxeo seguida de una conversión de referencia cada vez mayor
  • una conversión de unboxing (§5.1.8)
  • una conversión de unboxing seguida de una conversión primitiva cada vez mayor ".

Para ir de 'a'a Integer, necesitaríamos 1 ampliar el charvalor a un intcuadro luego inta Integer. Pero si observa las combinaciones de conversiones permitidas, no puede hacer una conversión primitiva de ampliación seguida de una conversión de boxeo.

Por lo tanto, 'a'a Integerque no está permitido. Esto explica el error de compilación en el primer caso.

Se podría pensar que 'a'al Byteno está permitida ya que se incurriría una conversión de restricción primitiva ... que no está en la lista en absoluto. De hecho, los literales son un caso especial. JLS 5.2 continúa diciendo lo siguiente.

"Además, si la expresión es una expresión constante ( §15.28 ) de tipo byte, short, char o int:

  • Se puede usar una conversión primitiva estrecha si la variable es de tipo byte, short o char, y el valor de la expresión constante es representable en el tipo de la variable.

  • Se puede usar una conversión primitiva de estrechamiento seguida de una conversión de boxeo si la variable es de tipo Byte,, Shorto Character, y el valor de la expresión constante es representable en el byte de tipo, short o char respectivamente ".

El segundo de estos se aplica 'a'a Byte, porque:

  • un carácter literal es una expresión constante y
  • el valor de 'a'es 97decimal, que está dentro del rango de byte( -128a +127).

Esto explica por qué no hay error de compilación en el segundo ejemplo.


1 - No podemos encajonar 'a'a Characteray luego ampliar Charactera Integerporque Characterno es un subtipo de Java Integer. Solo puede usar una conversión de referencia de ampliación si el tipo de origen es un subtipo del tipo de destino.

Stephen C
fuente
¿Podemos usar intcomo tipo de interruptor? (ya char -> intque está permitido el ensanchamiento primitivo)
AjahnCharles