Declarando e inicializando variáveis ​​em switches Java


99

Tenho uma pergunta maluca sobre os switches Java.

int key = 2;

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

Cenário 1 - Quando o keyé dois, ele imprime com sucesso o valor como 2.
Cenário 2 - Quando vou comentar value = 2, case 2:ele grita dizendo que o valor da variável local pode não ter sido inicializado .

Questões:

Cenário 1: Se o fluxo de execução não vai para case 1:(quando o key = 2), como ele sabe o tipo da variável de valor como int?

Cenário 2: Se o compilador conhece o tipo da variável de valor como int, então ele deve ter acessado a int value = 1;expressão em case 1:. (Declaração e Inicialização). Então, por que faz isso sqawrk Quando eu vou comentar value = 2no case 2:, dizendo que o O valor variável local pode não ter sido inicializado .


13
Não é uma pergunta maluca, é uma pergunta muito boa.
— biziclop

Possível duplicata do escopo
— Philippe Carriere

@PhilippeCarriere Na verdade, acho que deveria ser ao contrário - a resposta aqui é melhor (mesmo se a postagem for mais recente), pois há uma referência direta ao JLS e resume bem o assunto abordado em diferentes respostas nesse post. Veja também .
— Tunaki

@Tunaki A descrição de uma duplicata começa com "Esta pergunta já foi feita antes". Estou lendo isso porque o último deve ser marcado como uma duplicata do anterior. Mas concordo que este tem bons elementos. Talvez eles devessem ser mesclados de alguma forma?
— Philippe Carriere

Além disso, muitas perguntas sobre o SO estão marcadas como duplicatas da minha pergunta original, portanto, se você decidir que é melhor marcar esta como a nova original, corrija todos os links para fazer referência a este em vez do meu.
— Philippe Carriere

Respostas:


114

As declarações switch são estranhas em termos de escopo, basicamente. Da seção 6.3 do JLS :

O escopo de uma declaração de variável local em um bloco (§14.4) é o resto do bloco no qual a declaração aparece, começando com seu próprio inicializador e incluindo quaisquer declaradores adicionais à direita na declaração de declaração de variável local.

No seu caso, case 2está no mesmo bloco que case 1e aparece depois dele, embora case 1nunca seja executado ... então a variável local está no escopo e disponível para escrita, apesar de você logicamente nunca "executar" a declaração. (Uma declaração não é realmente "executável", embora a inicialização seja.)

Se você comentar a value = 2;atribuição, o compilador ainda saberá a qual variável você está se referindo, mas você não terá passado por nenhum caminho de execução que atribua um valor a ela, e é por isso que você obtém um erro como faria quando tentasse leia qualquer outra variável local não definida definitivamente.

Eu recomendo fortemente que você não use variáveis ​​locais declaradas em outros casos - isso leva a um código altamente confuso, como você viu. Quando introduzo variáveis ​​locais em instruções switch (o que tento fazer raramente - os casos devem ser muito curtos, de preferência), geralmente prefiro introduzir um novo escopo:

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

Acredito que isso seja mais claro.


11
+1 para "Uma declaração não é realmente" executável ", embora a inicialização seja.". E obrigado pelos conselhos também Skeet.
— namalfernandolk

1
Com o JEP-325 integrado, a imagem do escopo das variáveis ​​locais mudou e pode-se usar o mesmo nome em casos em vez de blocos de switch. Embora também dependa de codificação em bloco semelhante. Além disso, o valor atribuído a uma variável por caso de switch seria muito conveniente com as expressões de switch.
— Naman,

Pontos para adicionar um novo escopo com colchetes. Eu nem sabia que você poderia fazer isso.
— Floating Sunfish

21

A variável foi declarada (como um int), mas não inicializada (com um valor inicial atribuído). Pense na linha:

int value = 1;

Como:

int value;
value = 1;

A int valueparte diz ao compilador em tempo de compilação que você tem uma variável chamada valor que é um int. A value = 1parte o inicializa, mas isso acontece em tempo de execução e não acontece de forma alguma se esse branch do switch não for inserido.


+1 para a bela explicação de declaração e inicialização em tempo de compilação e tempo de execução.
— namalfernandolk

18

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

As declarações são processadas em tempo de compilação e não dependem do fluxo de execução do seu código. Uma vez que valueé declarado dentro do escopo local do bloco switch, ele pode ser usado em qualquer lugar daquele bloco a partir do ponto de sua declaração.


1
por que esta resposta está sendo votada positivamente? não responde à pergunta, ao contrário da resposta de paul ou skeet ...
— Dhruv Gairola

7
É verdade. Então, +1, um centavo, da minha parte também.
— Ravinder Reddy

3

Com a integração de JEP 325: Switch Expressions (Preview) em compilações de acesso antecipado JDK-12. Existem certas mudanças que podem ser vistas na resposta de Jon -

  1. Escopo da variável local - As variáveis ​​locais nos casos de switch podem agora ser locais para o próprio caso em vez de todo o bloco de switch . Um exemplo (semelhante ao que Jon tentou sintaticamente também) considerando aDayclasse enum para obter mais explicações:

    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. Switch Expressions - Se a intenção é atribuir um valor a uma variável e então fazer uso dele, uma vez pode fazer uso das expressões switch. por exemplo

    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

Esta explicação pode ajudar.

    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

Especificação Java:

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

O caso de conclusão abrupta por causa de uma quebra com um rótulo é tratado pela regra geral para declarações rotuladas (§14.7).

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

Declarações rotuladas:

LabeledStatement: Identifier: Statement

LabeledStatementNoShortIf: Identificador: StatementNoShortIf

Ao contrário de C e C ++, a linguagem de programação Java não tem instrução goto; rótulos de declaração de identificador são usados ​​com declarações break (§14.15) ou continue (§14.16) que aparecem em qualquer lugar dentro da declaração rotulada.

O escopo de um rótulo de uma instrução rotulada é a Instrução contida imediatamente.

Em outras palavras, caso 1, caso 2 são rótulos dentro da instrução switch. as instruções break e continue podem ser aplicadas aos rótulos.

Como os rótulos compartilham o escopo da instrução, todas as variáveis ​​definidas dentro dos rótulos compartilham o escopo da instrução switch.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.