Dichiarazione e inizializzazione di variabili all'interno di switch Java


99

Ho una domanda pazza sugli switch Java.

int key = 2;

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

Scenario 1 - Quando il keyè due esso stampa con successo il valore come 2.
Scenario 2 - Quando ho intenzione di commentare value = 2in case 2:esso squawks dicendo che la Il valore della variabile locale non sia stato inizializzato .

Domande:

Scenario 1: se il flusso di esecuzione non va a case 1:(quando il key = 2), come fa a conoscere il tipo di variabile di valore come int?

Scenario 2: se il compilatore conosce il tipo di variabile del valore come int, deve aver eseguito l'accesso int value = 1;all'espressione in case 1:. (Dichiarazione e inizializzazione). E allora perché lo fa sqawrk Quando ho intenzione di commentare value = 2in case 2:, dicendo che il Il valore della variabile locale non può essere stato inizializzato .


13
Non è una domanda folle, è un'ottima domanda.
biziclop

Possibile duplicato dell'ambito
Philippe Carriere

@PhilippeCarriere In realtà, penso che dovrebbe essere al contrario - la risposta qui è migliore (anche se il post è più recente) poiché c'è un riferimento diretto al JLS, e riassume bene il problema trattato in diverse risposte in quel post. Vedi anche .
Tunaki

@Tunaki La descrizione di un duplicato inizia con "Questa domanda è già stata posta". Sto leggendo che il secondo dovrebbe essere contrassegnato come un duplicato del precedente. Ma sono d'accordo sul fatto che questo abbia elementi carini. Forse dovrebbero essere unite in qualche modo?
Philippe Carriere

Anche molte domande su SO sono contrassegnate come duplicate della mia domanda originale, quindi se decidi che è meglio contrassegnare questa come nuova originale, correggi tutti i link in modo che facciano riferimento a questo invece del mio.
Philippe Carriere

Risposte:


114

Le istruzioni Switch sono strane in termini di scoping, fondamentalmente. Dalla sezione 6.3 del JLS :

Lo scopo di una dichiarazione di variabile locale in un blocco (§14.4) è il resto del blocco in cui appare la dichiarazione, a partire dal proprio inizializzatore e includendo eventuali ulteriori dichiaratori a destra nell'istruzione di dichiarazione della variabile locale.

Nel tuo caso, si case 2trova nello stesso blocco di case 1e appare dopo di esso, anche se case 1non verrà mai eseguito ... quindi la variabile locale è nell'ambito e disponibile per la scrittura nonostante tu logicamente non abbia mai "eseguito" la dichiarazione. (Una dichiarazione non è realmente "eseguibile" sebbene l'inizializzazione lo sia.)

Se commenti l' value = 2;assegnazione, il compilatore sa ancora a quale variabile ti riferisci, ma non avrai seguito alcun percorso di esecuzione che gli assegni un valore, motivo per cui ricevi un errore come faresti quando provi a leggere qualsiasi altra variabile locale non assegnata in modo definitivo.

Ti consiglio vivamente di non utilizzare variabili locali dichiarate in altri casi: porta a codice altamente confuso, come hai visto. Quando introduco variabili locali nelle istruzioni switch (cosa che cerco di fare raramente - i casi dovrebbero essere molto brevi, idealmente) di solito preferisco introdurre un nuovo ambito:

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

Credo che questo sia più chiaro.


11
+1 per "Una dichiarazione non è realmente" eseguibile "sebbene l'inizializzazione lo sia.". E grazie anche per i consigli Skeet.
namalfernandolk

1
Con il JEP-325 integrato, l' immagine dell'ambito delle variabili locali è cambiata e si può usare lo stesso nome in tutti i casi invece che nei blocchi di interruttori. Sebbene si basi anche su una codifica a blocchi simile. Inoltre, il valore assegnato a una variabile per caso switch sarebbe molto conveniente con le espressioni switch.
Naman

Punti per l'aggiunta di un nuovo ambito con parentesi graffe. Non sapevo nemmeno che potessi farlo.
Pesce luna galleggiante

21

La variabile è stata dichiarata (come int), ma non inizializzata (assegnato un valore iniziale). Pensa alla linea:

int value = 1;

Come:

int value;
value = 1;

La int valueparte dice al compilatore in fase di compilazione che hai una variabile chiamata valore che è un int. La value = 1parte lo inizializza, ma ciò accade in fase di esecuzione e non avviene affatto se quel ramo dello switch non viene inserito.


+1 per la bella spiegazione della dichiarazione e dell'inizializzazione in fase di compilazione e runtime.
namalfernandolk

18

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

Le dichiarazioni vengono elaborate in fase di compilazione e non dipendono dal flusso di esecuzione del codice. Poiché valueè dichiarato all'interno dell'ambito locale del blocco switch, è utilizzabile ovunque in quel blocco dal punto della sua dichiarazione.


1
perché questa risposta è stata votata? non risponde alla domanda, a differenza della risposta di paul o skeet ...
Dhruv Gairola

7
Lo fa. Quindi, +1, un penny, anche da parte mia.
Ravinder Reddy

3

Con l'integrazione di JEP 325: Switch Expressions (Preview) nelle build ad accesso anticipato JDK-12. Ci sono alcuni cambiamenti che potrebbero essere visti dalla risposta di Jon -

  1. Ambito delle variabili locali: le variabili locali nei casi degli interruttori possono ora essere locali rispetto al caso stesso invece dell'intero blocco degli interruttori . Un esempio (simile a quello che Jon aveva tentato anche sintatticamente) considerando laDayclasse enum per ulteriori spiegazioni:

    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. Espressioni di commutazione : se lo scopo è assegnare un valore a una variabile e quindi utilizzarlo, una volta è possibile utilizzare le espressioni di commutazione. per esempio

    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'
    }

0

Questa spiegazione potrebbe aiutare.

    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
        }
    }

0

Specifiche Java:

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

Il caso di completamento improvviso a causa di un'interruzione con un'etichetta è gestito dalla regola generale per le istruzioni etichettate (§14.7).

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

Dichiarazioni etichettate:

LabeledStatement: Identifier: Statement

LabeledStatementNoShortIf: Identificatore: StatementNoShortIf

A differenza di C e C ++, il linguaggio di programmazione Java non ha un'istruzione goto; Le etichette dell'istruzione identificatore vengono utilizzate con le istruzioni break (§14.15) o continue (§14.16) che compaiono ovunque all'interno dell'istruzione etichettata.

L'ambito di un'etichetta di un'istruzione etichettata è l'istruzione immediatamente contenuta.

In altre parole, il caso 1, il caso 2 sono etichette all'interno dell'istruzione switch. le istruzioni break e continue possono essere applicate alle etichette.

Poiché le etichette condividono l'ambito dell'istruzione, tutte le variabili definite all'interno delle etichette condividono l'ambito dell'istruzione switch.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.