javaswitch-statementpattern-matchingjava-21switch-expression

Does switch support objects other than Byte, Short, Character, Integer, enumerated types, and the String type?


JDK 7 added support for String with a switch, which extended the list of types supported by switch. As of Java 7, a switch supported the primitive types byte, short, char, and int, their respective wrapper types (Byte, Short, Character, and Integer), enumerated types, and the String type.

Does switch support objects other than Byte, Short, Character, Integer, enumerated types, and the String type?


Solution

  • Java 21

    In Java 16, JEP 394 extended the instanceof operator to take a type pattern and perform pattern matching. Leveraging this feature, in Java 21, JEP 441 made it possible to use case labels with patterns rather than just constants with switch statements and expressions.

    Demo:

    import java.time.*;
    import java.time.format.DateTimeFormatter;
    import java.util.Locale;
    
    class Main {
        private static final DateTimeFormatter FORMATTER =
                DateTimeFormatter.ofPattern("[MMMM dd, uuuu][ h:mm a][ VV][ XXX]")
                        .localizedBy(Locale.ENGLISH);
    
        static String format(Object obj) {
            return switch (obj) {
                case null -> "🤔";
                case LocalDate date -> date.format(FORMATTER);
                case LocalTime time -> time.format(FORMATTER).trim();
                case OffsetDateTime odt -> odt.format(FORMATTER);
                case ZonedDateTime zdt -> zdt.format(FORMATTER);
                default -> obj.toString();
            };
        }
    
        public static void main(String[] args) {
            // Some sample values
            ZoneId zone = ZoneId.of("America/New_York");
            ZoneOffset offset = ZoneOffset.of("+05:30");
            LocalDate date = LocalDate.of(2025, Month.FEBRUARY, 21);
            LocalTime time = LocalTime.of(20, 30, 40);
            OffsetDateTime odt = OffsetDateTime.of(date, time, offset);
            ZonedDateTime zdt = ZonedDateTime.of(date, time, zone);
    
            // Test format
            System.out.println(format(date));
            System.out.println(format(time));
            System.out.println(format(odt));
            System.out.println(format(zdt));
            System.out.println(format(null));
            System.out.println(format(Instant.now()));
        }
    }
    

    Output from a sample run:

    February 21, 2025
    8:30 PM
    February 21, 2025 8:30 PM +05:30
    February 21, 2025 8:30 PM America/New_York -05:00
    🤔
    2025-02-22T11:25:36.329272700Z
    

    Guard clauses and case dominance

    It also allows a guard clause with a case in the form case <pattern> where <guard clause>. The case dominance rule must be maintained between cases with a guard clause. As per this rule, a more-dominant case must be below a less-dominant case. It is analogous to catch blocks and the order of caught exceptions.

    Demo:

    class Main {
        enum Day {MON, TUE, WED, THU, FRI, SAT, SUN}
    
        public static void main(String[] args) {
            Object[] values = {
                    -10, 20, false, Day.SAT, 30.0, Double.NaN, Day.THU, Day.SUN
            };
            for (Object obj : values) {
                switch (obj) {
                    case Integer i when i > 0 -> 
                            System.out.println("A positive integer");
                    case Integer i when i < 0 -> 
                            System.out.println("A negative integer");
                    case Double d when d.isNaN() -> 
                            System.out.println("0.0/0.0");
                    case Double d -> 
                            System.out.println("Double - Not NaN");
                    case Day d when d == Day.SAT || d == Day.SUN -> 
                            System.out.println("Weekend");
                    case Day d -> System.out.println("Weekday");
                    case null, default -> 
                            System.out.println("Discard");
                }
            }
        }
    }
    

    Output:

    A negative integer
    A positive integer
    Discard
    Weekend
    Double - Not NaN
    0.0/0.0
    Weekday
    Weekend
    

    ⚠️Warning: The order of case Double d when d.isNaN() and case Double d is important. It is because case Double d dominates over case Double d when d.isNaN() and therefore putting case Double d when d.isNaN() below case Double d will make case Double d when d.isNaN() unreachable. Similar is the case with case Day d when d == Day.SAT || d == Day.SUN and case Day d.