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.