Declarar e inicializar variables dentro de los conmutadores de Java

99

Tengo una pregunta loca sobre los conmutadores de Java.

int key = 2;

switch (key) {
    case 1:
        int value = 1;
        break;
    case 2:
        value = 2;
        System.out.println(value);
        break;
    default:
        break;
}

Escenario 1 - Cuando el keyes dos se imprime con éxito el valor 2.
Escenario 2 - Cuando voy a comentar value = 2en el case 2:que chilla diciendo que el valor de la variable local no puede haber sido inicializado .

Preguntas:

Escenario 1: Si el flujo de ejecución no va a case 1:(cuando key = 2), ¿cómo sabe el tipo de variable de valor como int?

Escenario 2: Si el compilador conoce el tipo de variable de valor como int, entonces debe haber accedido a la int value = 1;expresión en case 1:. (Declaración e Inicialización). Entonces ¿por qué lo hace sqawrk Cuando voy a comentar value = 2en case 2:, diciendo que el valor de la variable local no puede haber sido inicializado .

namalfernandolk
fuente
13
No es una pregunta loca, es una muy buena pregunta.
biziclop
Posible duplicado del alcance
Philippe Carriere
@PhilippeCarriere En realidad, creo que debería ser al revés: la respuesta aquí es mejor (incluso si la publicación es más nueva) ya que hay una referencia directa a JLS, y resume bien el tema cubierto en diferentes respuestas en esa publicación. Vea también .
Tunaki
@Tunaki La descripción de un duplicado comienza con "Esta pregunta se ha hecho antes". Estoy leyendo eso, ya que el último debería estar marcado como un duplicado del anterior. Pero estoy de acuerdo en que este tiene buenos elementos. ¿Quizás deberían fusionarse de alguna manera?
Philippe Carriere
Además, muchas preguntas sobre SO están marcadas como duplicadas de mi pregunta original, por lo que si decide que es mejor marcar esta como la nueva original, corrija todos los enlaces para hacer referencia a esta en lugar de a la mía.
Philippe Carriere

Respuestas:

114

Las declaraciones de cambio son extrañas en términos de alcance, básicamente. De la sección 6.3 de la JLS :

El alcance de una declaración de variable local en un bloque (§14.4) es el resto del bloque en el que aparece la declaración, comenzando con su propio inicializador e incluyendo cualquier otro declarador a la derecha en la declaración de declaración de variable local.

En su caso, case 2está en el mismo bloque que case 1y aparece después, aunque case 1nunca se ejecutará ... por lo que la variable local está dentro del alcance y disponible para escribir a pesar de que, lógicamente, nunca "ejecute" la declaración. (Una declaración no es realmente "ejecutable", aunque la inicialización sí lo es).

Si comenta la value = 2;asignación, el compilador aún sabe a qué variable se refiere, pero no habrá pasado por ninguna ruta de ejecución que le asigne un valor, por lo que obtiene un error como lo haría cuando intenta leer cualquier otra variable local no asignada definitivamente.

Le recomendaría encarecidamente que no utilice variables locales declaradas en otros casos, ya que conduce a un código muy confuso, como ha visto. Cuando introduzco variables locales en declaraciones de cambio (lo que trato de hacer rara vez; los casos deberían ser muy cortos, idealmente), generalmente prefiero introducir un nuevo alcance:

case 1: {
    int value = 1;
    ...
    break;
}
case 2: {
    int value = 2;
    ...
    break;
}

Creo que esto está más claro.

Jon Skeet
fuente
11
+1 para "Una declaración no es realmente" ejecutable "aunque la inicialización sí lo es". Y gracias por los consejos también Skeet.
Namalfernandolk
1
Con el JEP-325 integrado, la imagen del alcance de las variables locales ha cambiado y se puede usar el mismo nombre en todos los casos en lugar de bloques de interruptores. Aunque también se basa en una codificación de bloques similar. Además, el valor asignado a una variable por caso de cambio sería muy conveniente con las expresiones de cambio.
Naman
Puntos por agregar un nuevo alcance con llaves. Ni siquiera sabía que podía hacer eso.
Pez luna flotante
21

La variable se ha declarado (como un int), pero no se ha inicializado (se le asignó un valor inicial). Piense en la línea:

int value = 1;

Como:

int value;
value = 1;

La int valueparte le dice al compilador en tiempo de compilación que tienes una variable llamada valor que es un int. La value = 1parte lo inicializa, pero eso sucede en tiempo de ejecución y no sucede en absoluto si no se ingresa esa rama del interruptor.

Pablo
fuente
+1 para la buena explicación de la declaración y la inicialización en tiempo de compilación y tiempo de ejecución.
Namalfernandolk
18

De http://www.coderanch.com/t/447381/java-programmer-SCJP/certification/variable-initialization-within-case-block

Las declaraciones se procesan en tiempo de compilación y no dependen del flujo de ejecución de su código. Dado que valuese declara dentro del alcance local del bloque de conmutación, es utilizable en cualquier lugar de ese bloque desde el punto de su declaración.

Basura
fuente
1
¿Por qué se vota a favor esta respuesta? no responde la pregunta, a diferencia de la respuesta de paul o skeet ...
Dhruv Gairola
7
Lo hace. Entonces, +1, un centavo, de mi parte también.
Ravinder Reddy
3

Con la integración de JEP 325: Switch Expressions (Preview) en las compilaciones de acceso temprano JDK-12. Hay ciertos cambios que se pueden ver en la respuesta de Jon :

  1. Alcance de variable local : las variables locales en las cajas de interruptores ahora pueden ser locales para la caja en sí en lugar de todo el bloque de interruptores . Un ejemplo (similar a lo que Jon también había intentado sintácticamente) considerando laDayclase enum para una explicación más detallada:

    public enum Day {
        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }
    
    // some another method implementation
    Day day = Day.valueOf(scanner.next());
    switch (day) {
        case MONDAY,TUESDAY -> {
            var temp = "mon-tue";
            System.out.println(temp);
        }
        case WEDNESDAY,THURSDAY -> {
            var temp = Date.from(Instant.now()); // same variable name 'temp'
            System.out.println(temp);
        }
        default ->{
            var temp = 0.04; // different types as well (not mandatory ofcourse)
            System.out.println(temp);
        }
    }
  2. Expresiones de cambio : si la intención es asignar un valor a una variable y luego hacer uso de él, una vez puede hacer uso de las expresiones de cambio. p.ej

    private static void useSwitchExpression() {
        int key = 2;
        int value = switch (key) {
            case 1 ->  1;
            case 2 -> 2;
            default -> {break 0;}
        };
        System.out.println("value = " + value); // prints 'value = 2'
    }
Naman
fuente
0

Esta explicación podría ayudar.

    int id=1;

    switch(id){
        default: 
            boolean b= false; // all switch scope going down, because there is no scope tag

        case 1:
            b = false;
        case 2:{
            //String b= "test"; you can't declare scope here. because it's in the scope @top
            b=true; // b is still accessible
        }
        case 3:{
            boolean c= true; // case c scope only
            b=true; // case 3 scope is whole switch
        }
        case 4:{
            boolean c= false; // case 4 scope only
        }
    }
Java jansen
fuente
0

Especificaciones de Java:

https://docs.oracle.com/javase/specs/jls/se12/html/jls-14.html#jls-14.11

El caso de finalización abrupta debido a una ruptura con una etiqueta es manejado por la regla general para declaraciones etiquetadas (§14.7).

https://docs.oracle.com/javase/specs/jls/se12/html/jls-14.html#jls-14.7

Declaraciones etiquetadas:

Declaración etiquetada: Identificador: Declaración

LackedStatementNoShortIf: Identificador: StatementNoShortIf

A diferencia de C y C ++, el lenguaje de programación Java no tiene instrucción goto; Las etiquetas de declaración de identificación se utilizan con declaraciones de ruptura (§14.15) o continuar (§14.16) que aparecen en cualquier lugar dentro de la declaración etiquetada.

El alcance de una etiqueta de una declaración etiquetada es la Declaración contenida inmediatamente.

En otras palabras, el caso 1, el caso 2 son etiquetas dentro de la instrucción de cambio. Las declaraciones break y continue se pueden aplicar a las etiquetas.

Como las etiquetas comparten el alcance de la declaración, todas las variables definidas dentro de las etiquetas comparten el alcance de la declaración de cambio.

Pavel Molchanov
fuente