A instrução switch
Agora, expandimos nossa discussão de declarações if-then-else discutindo a instrução switch. Uma instrução switch, como mostrado na Figura 2.4, é uma complexa estrutura de tomada de decisão na qual um único valor é avaliado e o fluxo é redirecionado para a primeira ramificação correspondente, conhecido como uma declaração de caso. Se nenhuma declaração de caso for encontrada que corresponda ao valor, um declaração padrão opcional será chamada. Se essa opção padrão não estiver disponível, toda a instrução switch será ignorada.
Tipos de dados suportados
Como mostrado na Figura 2.4, uma instrução switch possui uma variável de destino que não é avaliada em tempo de execução. Antes do Java 5.0, essa variável só poderia ser de valores int ou aqueles valores que poderiam ser promovidos para int, especificamente byte, short, char ou int . Quando enum foi adicionado em Java 5.0, o suporte foi adicionado para alternar instruções para suportar valores enum. No Java 7, as instruções switch foram atualizadas para permitir a correspondência nos valores de String . Finalmente, a instrução também suporta qualquer uma das classes de invólucro numérico primitivo, como Byte, Short, Character ou Integer.
Os tipos de dados suportados pelas instruções switch incluem o seguinte:
■ int e Integer
■ byte e Byte
■ short e Short
■ char e Character
■ int e Integer
■ String
■ valores enum
Para o exame, recomendamos que você memorize essa lista. Note que booleano e long, e suas classes embaralhadas associadas, não são suportadas pelas instruções switch.
Valores constantes de tempo de compilação
Os valores em cada declaração de caso devem ser valores constantes de tempo de compilação dos mesmos dados digite como o valor do switch. Isso significa que você pode usar somente literais, constantes enum ou variáveis final constantes do mesmo tipo de dados. Por constante final, queremos dizer que a variável deve ser marcada com o modificador final e inicializado com um valor literal na mesma expressão em que é declarada.
Vejamos um exemplo simples usando o dia da semana, com 0 para Sunday, 1 para Monday e assim por diante:
int dayOfWeek = 5;
switch(dayOfWeek) {
default:
System.out.println("Weekday");
break;
case 0:
System.out.println("Sunday");
break;
case 6:
System.out.println("Saturday");
break;
}
Com um valor de dayOfWeek de 5 , esse código produzirá:
Weekday
A primeira coisa que você pode notar é que há uma declaração break no final de cada caso e seção default. Discutiremos detalhadamente as instruções break quando discutirmos loops, mas por enquanto, tudo que você precisa saber é que eles terminam a instrução switch e o fluxo de controle retorna para a declaração anexa. Como veremos em breve, se você deixar de fora a declaração do break, o fluxo continuará para o próximo case ou para o bloco padrão automaticamente.
Outra coisa que você pode notar é que o bloco padrão não está no final do switch declaração. Não há exigência de que o caso ou as declarações padrão estejam em uma determinada ordem, a menos que você tenha caminhos que atinjam várias seções do bloco switch em uma única execução.
Para ilustrar os dois pontos anteriores, considere a seguinte variação:
int dayOfWeek = 5;
switch(dayOfWeek) {
case 0:
System.out.println("Sunday");
default:
System.out.println("Weekday");
case 6:
System.out.println("Saturday");
break;
}
Este código se parece muito com o exemplo anterior, exceto que duas das instruções break foram removidas e a ordem foi alterada. Isso significa que, para o valor dado de dayOfWeek, 5, o código irá saltar para o bloco default e, em seguida, executar todos os procedimentos e declarações de case em ordem até encontrar uma declaração break ou terminar a estrutura:
Weekday
Saturday
A ordem do case e as declarações default agora são importantes, pois declaração default no final da instrução switch causaria apenas uma palavra a ser saída.
E se o valor de dayOfWeek fosse 6 neste exemplo? O bloco padrão ainda seria executado? A saída deste exemplo com dayOfWeek configurada com 6 seria:
Saturday
Embora o bloco padrão tenha sido declarado antes dos blocos case, apenas o bloco de case foi executado. Se você recordar a definição do bloco default, só será ramificado se não houver valor de caso correspondente para a instrução switch, independentemente de sua posição dentro do switch.
Finalmente, se o valor de dayOfWeek for 0 , todas as três instruções serão exibidas:
Sunday
Weekday
Saturday
Observe que neste último exemplo, o default é executado, pois não houve break no final do bloco de casos anteriores. Enquanto o código não se ramifica para a instrução default, se houver um valor de caso correspondente dentro da instrução switch, ele executará a declaração default se a encontrar após uma declaração de caso para a qual não há declaração break.
Os criadores do exame gostam de trocar exemplos que estão faltando instruções break! Ao avaliar instruções switch no exame, considere sempre que vários ramos podem ser visitados em uma única execução.
Concluímos nossa discussão sobre instruções switch reconhecendo que o tipo de dados para instruções de caso, todas devem corresponder ao tipo de dados da variável de comutação. Como já discutimos, o valor da declaração de caso também deve ser uma literal, constante de enumeração ou constante final da variável. Por exemplo, dada a seguinte instrução switch , observe quais instruções de case irá compilar e quais não irão:
private int getSortOrder(String firstName, final String lastName) {
String middleName = "Patricia";
final String suffix = "JR";
int id = 0;
switch(firstName) {
case "Test":
return 52;
case middleName: // DOES NOT COMPILE
id = 5;
break;
case suffix:
id = 0;
break;
case lastName: // DOES NOT COMPILE
id = 8;
break;
case 5: // DOES NOT COMPILE
id = 7;
break;
case 'J': // DOES NOT COMPILE
id = 10;
break;
case java.time.DayOfWeek.SUNDAY: // DOES NOT COMPILE
id=15;
break;
}
return id;
}
A primeira instrução de caso compila sem problemas usando um literal de string e é um bom exemplo de como uma instrução de retorno, como uma instrução break, pode ser usada para sair do switch antecipada. A segunda instrução case não compila porque middleName não é uma variável final , apesar de ter um valor conhecido nesta linha de execução específica. A terceira instrução case compila sem problema porque o sufixo é uma variável constante final.
No quarto caso , apesar de lastName ser final , não é constante como foi passado para a função; portanto, essa linha não compila também. Finalmente, as últimas três instruções case não compilam porque nenhuma delas possui um tipo correspondente de String; a última é um valor enum.