Recentemente voltei a mexer mais com Java devido ao trabalho e também alguns conteúdos que eu estava acompanhando que utilizam a linguagem. Como estou utilizando o IntelliJ como IDE ela sugeriu algo muito interessante e que passei adotar sempre que posso, o nova sintaxe do switch/case.
A instrução switch case comum do Java segue algo muito similar a já implementada no C, um exemplo simples:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| package com.company;
public class Main {
public static void main(String[] args) {
var nome = "Cebolinha";
switch(nome) {
case "Lucas":
System.out.println("Bem vindo programador Lucas");
break;
case "Grace":
System.out.println("Bem vinda programadora Grace Hopper");
break;
default:
System.out.println("Bem vindx pessoa programadora " + nome);
}
}
}
|
Esta é a estrutura comum que já conhecemos, como você pode ver temos os casos específicos que tratamos, o caso padrão e para que a avaliação do switch/case pare precisamos utilizar a clausula break, o que chega a me dar “gastura” em switch/cases muito grandes.
Algumas outras linguagens (Rust, Scala e outras) tem uma síntaxe construida sobre casamento de padrões e implementam uma estrutura similar porém um pouco mais expressiva e eficiente. No Java 13 algo similar (mas ainda não tão poderoso) foi incluído e agora podemos ter uma nova síntaxe que utiliza o operador -> (seta) e já mostra a diferença:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| package com.company;
public class Main {
public static void main(String[] args) {
var nome = "Cebolinha";
switch (nome) {
case "Lucas" -> System.out.println("Bem vindo programador Lucas");
case "Grace" -> System.out.println("Bem vinda programadora Grace Hopper");
default -> System.out.println("Bem vindx pessoa programadora " + nome);
}
}
}
|
Agora já não é mais necessário utilizar o break para encerrar a busca, pois quando houver um caso que seja positivo ele executa e encerra a avaliação. Para casos de avaliação de Enums fica muito interessante, como podemos ver no exemplo abaixo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| package com.company;
public class Main {
public enum Veiculos {
BICICLETA, MOTO, CARRO, ONIBUS, CAMINHAO
}
public static void main(String[] args) {
var veiculo = Veiculos.CAMINHAO;
float valorPedagio = 0;
switch (veiculo) {
case MOTO -> valorPedagio = 1;
case CARRO -> valorPedagio = 2;
case CAMINHAO -> valorPedagio = 3;
case ONIBUS -> valorPedagio = 4;
}
System.out.println("Você deverá pagar um pedágio de " + valorPedagio);
}
}
|
O ponto que talvez seja mais interessante é que o switch/case passa a ser também uma expressão, ou seja, pode retornar valores, para isso basta utilizarmos numa atribuição, reduzindo bastante o caso anterior:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| package com.company;
public class Main {
public enum Veiculos {
BICICLETA, MOTO, CARRO, ONIBUS, CAMINHAO
}
public static void main(String[] args) {
var veiculo = Veiculos.CAMINHAO;
float valorPedagio = switch (veiculo) {
case MOTO -> valorPedagio = 1;
case CARRO -> valorPedagio = 2;
case CAMINHAO -> valorPedagio = 3;
case ONIBUS -> valorPedagio = 4;
default -> 0; // Como não cobrimos todos os casos eu preciso definir um default
}; // Por ser uma expressão precisa terminar com ;
System.out.println("Você deverá pagar um pedágio de " + valorPedagio);
}
}
|
Para garantir que a variável fique corretamente preenchida (já pensou caso não batesse com nenhum valor? Seria algo indefinido) é obrigatório avaliar todos os casos, no caso de umde Enums é necessário cobrir todos os casos nem que seja definindo um default. Outros tipos basta incluir um caso default, como String e tipos numéricos.
Nos exemplos anteriores só utilizamos expressões de uma linha nos cases, mas é possível utilizar blocos também para computar algo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| package com.company;
public class Main {
public enum Veiculos {
BICICLETA(0), MOTO(1), CARRO(2), ONIBUS(4), CAMINHAO(3);
private final float tarifa;
Veiculos(float tarifa) {
this.tarifa = tarifa;
}
}
public static void main(String[] args) {
var veiculo = Veiculos.CAMINHAO;
float valorPedagio = switch (veiculo) {
case MOTO, CARRO -> veiculo.tarifa;
case ONIBUS, CAMINHAO -> {
var adicional = 5;
yield veiculo.tarifa + adicional;
}
default -> 0;
};
System.out.println("Você deverá pagar um pedágio de " + valorPedagio);
}
}
|
Para retornar um valor para o switch nos casos de blocos utilizamos a palavra yield (que fica similar ao return de uma função).
Ver essa nova sintaxe aliada as novidades que foram incluídas desde o Java 8 irão permitir um código muito mais expressivo e coeso. Além disso no recém lançado Java 17 está sendo incluída uma proposta de casamento de padrões (pattern matching) que irá aumentar ainda mais o poder do switch/case, pois além de avaliar valores também será capaz de avaliar tipos, iremos comentar mais sobre isso em um post futuro.