DEV Community

Cover image for "Pesquei, Java!": Transformações do Switch: Novidades e Recursos
Bárbara Salla
Bárbara Salla

Posted on

"Pesquei, Java!": Transformações do Switch: Novidades e Recursos

O switch no Java, desde quando surgiu, já passou por várias atualizações, tornando-se cada vez mais versátil e poderoso. Essas melhorias impactaram tanto o comportamento, sintaxe, exaustividade e a capacidade de trabalhar com diferentes tipos de dados.

Inicialmente, o switch foi introduzido como uma declaração, e, mais tarde, evoluiu para incluir o conceito de expressão em versões mais recentes. Para entender melhor o que esses dois conceitos significam, podemos descreve-los como:

  • Declaração: É usada para controlar o fluxo do programa, executando diferentes blocos de código com base no valor de uma variável. Não retorna diretamente um valor.
  • Expressão: Avalia uma condição e retorna um valor diretamente, que pode ser atribuído a uma variável, por exemplo.

A seguir, exploraremos as mudanças ao longo das versões do Java e suas implicações práticas.

1.Switch como Declaração (Forma Clássica)

Quando falamos do switch como declaração, estamos nos referindo à forma clássica de uso dessa estrutura. Em versões anteriores do Java, o switch só era utilizado apenas como uma declaração básica de controle de fluxo. Ele podia ser usado com tipos int, char, byte, short e outros, mas sempre sem retornar um valor diretamente.

Limitações da forma clássica do switch:

  • Tipos restritos: Apenas funcionava com tipos primitivos numéricos (int, short, char, byte) e, posteriormente, com String (a partir do Java 7) e enums (Java 5).
  • Repetitividade: Não permitia a reutilização de lógica ou a utilização de blocos mais compactos.
  • Inflexibilidade: Não suportava expressões complexas ou a possibilidade de retornar diretamente valores.
  • Escalabilidade: O switch clássico era inadequado para cenários em que era necessário lidar com múltiplos tipos de dados ou condições compostas, como intervalos de valores.

Exemplo: Switch clássico com números inteiros

public static void main(String[] args) {
    var dayValue = 1;
    String day = "";

    switch (dayValue){
        case 1:
            day = "sunday";
            break;
        case 2:
            day = "monday";
            break;
        case 3:
            day = "Tuesday";
            break;
        case 4:
            day = "Wednesday";
            break;
        case 5:
            day = "Thursday";
            break;
        case 6:
            day = "Friday";
            break;
        case 7:
            day = "Saturday";
            break;
        default:
            System.out.println("Invalid Value: "+ dayValue);
    }

    System.out.println("\nValue: "+dayValue+"\nDay: "+ day+"");
}
Enter fullscreen mode Exit fullscreen mode

Resposta do console:

Image description

Nesse cenário, por exemplo, se retirarmos o break, o programa executará todos os blocos a partir do caso correspondente ao valor, resultando no comportamento que conhecemos como "Fall Through".

public static void main(String[] args) {
        var dayValue = 1;
        String day = "";

        switch (dayValue){
            case 1:
                day = "Sunday";
            case 2:
                day = "Monday";
            case 3:
                day = "Tuesday";
            case 4:
                day = "Wednesday";
            case 5:
                day = "Thursday";
            case 6:
                day = "Friday";
            case 7:
                day = "Saturday";
            default:
                System.out.println("Invalid Value: "+ dayValue);
        }

        System.out.println("\nValue: "+dayValue+"\nDay: "+ day+"");
    }
Enter fullscreen mode Exit fullscreen mode

Resposta do console:

Image description

2.Suporte a String no Switch (Java 7):

A partir do Java 7, podemos então passar valores do tipo String como parâmetro no switch, ampliando significativamente o seus casos de uso, por exemplo:

public static void main(String[] args) {
        String day = "Monday";

        switch (day) {
            case "Monday":
                System.out.println("Start of the work week.");
                break;
            case "Tuesday":
            case "Wednesday":
            case "Thursday":
                System.out.println("Midweek days.");
                break;
            case "Friday":
                System.out.println("Almost the weekend!");
                break;
            case "Saturday":
            case "Sunday":
                System.out.println("Weekend!");
                break;
            default:
                System.out.println("Invalid day.");
        }
}
Enter fullscreen mode Exit fullscreen mode

Resposta do console:

Image description

3.Switch com Expressões (Java 12+):

O Java 12 introduziu as Switch Expressions (inicialmente como recurso preview). Com isso, o switch ganhou uma nova sintaxe e a capacidade de permitir ao switch retornar diretamente valores a uma variável, eliminando a necessidade de utilizar o break. No exemplo a seguir, podemos observar a implementação direta do switch após o sinal de atribuição de valor (=) da variável day.

 public static void main(String[] args) {
        int dayValue = 6;
        String day = switch (dayValue){
            case 1 -> "Sunday";
            case 2 -> "Monday";
            case 3 -> "Tuesday";
            case 4 -> "Wednesday";
            case 5 -> "Thursday";
            case 6 -> "Friday";
            case 7 -> "Saturday";
            default -> "invalid day.";
        };

        System.out.println("\nValue: "+dayValue+"\nDay: "+ day);
    }
Enter fullscreen mode Exit fullscreen mode

Resposta do console:

Image description

4.Uso do Yield (Java 13/14)

O yield foi introduzido no Java 13 como parte das Switch Expressions, sendo apenas oficializado no Java 14. O termo yield é usado para retornar um valor em bloco de código dentro do switch. Tornando possível usar blocos de código mais complexos e retornar valores a partir deles.

public String getDayType(int day) {
    String dayType = switch (day) {
        case 1 -> "Monday";
        case 2 -> "Tuesday";
        case 3 -> "Wednesday";
        default -> {
            System.out.print("Invalid day");
            yield null;
        }
    };
    return dayType;
}
Enter fullscreen mode Exit fullscreen mode

No exemplo acima, o yield permite adicionar blocos de código em um case usando {} e retornar valores específicos ao final do processamento, tornando possível implementar diferentes comportamentos para os diversos cases. Diferentemente do já conhecido return, que sai e finaliza imediatamente a execução do método, o yield apenas retorna um valor para a expressão switch.

5.Múltiplos Valores por Case (Java 14)

Com o Java 14, as Switch Expressions foram formalizadas e, além disso, tornou-se possível combinar vários valores em um único case, simplificando condições que compartilham o mesmo comportamento, como no exemplo abaixo:

public static void main(String[] args) {
            String day = "Monday";
            switch (day) {
                case "Saturday", "Sunday":
                    System.out.println("Weekend");
                    break;
                case "Monday", "Tuesday", "Wednesday", "Thursday":
                    System.out.println("Weekday");
                    break;
                default:
                    System.out.println("Invalid day");
            }
}
Enter fullscreen mode Exit fullscreen mode

Resposta do console:

Image description

6.Exaustividade no Switch (Java 12 e Java 14)

Com o switch e as Switch Expressions, se os valores possíveis forem completamente definidos, como quando trabalhamos com Enum, o compilador pode verificar se todos os casos foram cobertos, garantindo maior controle e segurança na implementação. Assim, o compilador consegue interpretar se todas as opções foram tratadas nos cases e sinalizar caso não esteja cobrindo todas as possibilidades, evitando comportamentos inesperados em tempo de execução.

Portanto, se todos os casos não forem cobertos, o compilador emitirá um erro de compilação, como mostrado no exemplo abaixo:

public class Main {
    enum Day {
        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }

    public static void main(String[] args) {
        Day day = Day.FRIDAY;
        System.out.println("This day falls on a  " + getDayType(day));
    }

    public static String getDayType(Day day) {
        return switch (day) {
            case MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY -> "Weekday";
            case SATURDAY -> "Weekend";
        };
    }
}
Enter fullscreen mode Exit fullscreen mode

Erro de compilação:

Image description

Já com todas as opções tratadas, o código compila e roda conforme esperado:

public class Main {
    enum Day {
        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }

    public static void main(String[] args) {
        Day day = Day.FRIDAY;
        System.out.println("This day falls on a  " + getDayType(day));
    }

    public static String getDayType(Day day) {
        return switch (day) {
            case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Weekday";
            case SATURDAY, SUNDAY -> "Weekend";
        };
    }
}
Enter fullscreen mode Exit fullscreen mode

Resposta do console:

Image description

7.Exaustividade no switch com Classes Seladas (Java 17)

A exaustividade no switch também se aplica a classes seladas a partir do Java 17. Uma classe selada controla diretamente quais outras classes ou interfaces podem estendê-las ou implementá-las, limitando a hierarquia de herança, permitindo que o compilador verifique se todos os casos possíveis foram tratados.

sealed class Day permits Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday { }

final class Monday extends Day { }
final class Tuesday extends Day { }
final class Wednesday extends Day { }
final class Thursday extends Day { }
final class Friday extends Day { }
final class Saturday extends Day { }
final class Sunday extends Day { }

public class Main {
    public static void main(String[] args) {
        Day day = new Monday(); 
        System.out.println("Today is: " + dayType(day));
    }

    public static String dayType(Day day) {
        return switch (day) {
            case Tuesday t -> "Weekday";
            case Friday f -> "Weekday";
            case Saturday s -> "Weekend";
        };
    }
}
Enter fullscreen mode Exit fullscreen mode

Conforme o exemplo acima, verificamos que:

  • Temos uma classe selada Day que permite apenas que as classe Monday, Tuesday, Wednesday, Thursday, Friday, Saturday e Sunday herdam seus atributos e comportamentos.
  • Temos no método main a chamada de um método (dayType()) que utiliza o switch com classes seladas.
  • Não temos todas as opções de subclasses da Classe Day contempladas nos cases do switch, resultando num erro de compilação:

Image description

8.Pattern Matching no Switch (Java 17)

Outra novidade introduzida no Java 17 foi a integração do switch ao recurso Pattern Matching, permitindo verificar se determinado objeto é do tipo de uma classe especifica a partir de um switch.

Antes do Java 17, a verificação de tipos era feita com instanceof e exigia blocos separados de código, como no exemplo abaixo:

public static void main(String[] args) {
        Object object = "text";

        if(object instanceof String){
            System.out.println("This object is a String");
        } else if (object instanceof Long) {
            System.out.println("This object is a long");
        } else {
            System.out.println("Unidentified object type");
        }
}
Enter fullscreen mode Exit fullscreen mode

Resposta console:

Image description

Já quando aplicamos o Pattern Matching com switch temos a seguinte sintaxe:

public static void main(String[] args) {
    Object object = "text";

    switch (object){
        case String s-> System.out.println("This object is a String");
        case Integer i ->  System.out.println("This object is a Integer");
        default -> System.out.println("Unidentified object type");
    }
}
Enter fullscreen mode Exit fullscreen mode

Resposta console:

Image description

Esse recurso facilita a verificação de tipos, evitando a necessidade do instanceof e de blocos separados para diferentes tipos de objetos.

9.Uso de null como case (Java 17)

A partir do Java 17, tornou-se possível atribuir valor null a um case no switch, por exemplo:

     case null -> System.out.println("This is a null object");
Enter fullscreen mode Exit fullscreen mode

Espero que essas informações tenham sido úteis para você e agreguem ao seu código. Até mais!

Top comments (0)