Skip to content

Switch tutorials out of date for Java SE 21 addition of null selector value support, pattern matching, guards, any reference type in the selector expression, and more #215

@willy-b

Description

@willy-b

Hello dev.java team!

I write regarding some out of date content on the Switch tutorials at

These articles on Switch are out of date for features released in the long-term-supported Java release Java SE 21 (2023), specifically null selector variable value and pattern matching support for switch blocks and selector expressions of any reference type ( https://openjdk.org/jeps/441 ). The first article also omits mention that multiple case constants per switch label, even for old fashioned switch statements without switch rule arrow syntax, are supported since Java SE 14 (shows case 1: case 2: case 3: style instead of case 1, 2, 3:).


As of 2026-03-21 "Branching with Switch Statements" https://dev.java/learn/language-basics/switch-statement/ (archived as is at https://web.archive.org/web/20260321191854/https://dev.java/learn/language-basics/switch-statement/ ; note page unchanged since 2026-03-12, so 2026-03-21 forwards to 2026-03-12 snapshot) ,
says that null values of the selector variable will necessarily result in a NullPointerException and should be avoided in switch statements:

Null Selector Variables
The selector variable of a switch statement can be an object, so this object can be null. You should protect your code from null selector variables, because in this case the switch statement will throw a NullPointerException.

This does not mention that as of 2023, in Java 21, support for null values has been added to switch statements via the "case null" and "case null, default" labels.
Compare to
https://docs.oracle.com/javase/specs/jls/se21/html/jls-14.html#jls-14.11.1 , where in Java 21 "case null" and "case null, default" label support has been added:

SwitchLabel:
case CaseConstant {, CaseConstant}
case null [, default]
case CasePattern [Guard]
default

Image

The new features of pattern matching and case refinement via guards with switch are also not mentioned.

Note that "Branching with Switch Expressions" https://dev.java/learn/language-basics/switch-expression/ (archived as is at https://web.archive.org/web/20260321202312/https://dev.java/learn/language-basics/switch-expression/ ) mentions that null selector variable value support is available for preview in Java SE 17 but again this has been released as of 2023 in Java 21 (for Switch statements AND expressions), so the expressions page should also be updated.

I notice that part of this was reported also at #100 .


Also,
in "Branching with Switch Statements" https://dev.java/learn/language-basics/switch-statement/ (archived as is at https://web.archive.org/web/20260321191854/https://dev.java/learn/language-basics/switch-statement/ ; note page unchanged since 2026-03-12, so 2026-03-21 forwards to 2026-03-12 snapshot)
it says:

You must choose the type of your selector variable among the following types:

byte, short, char, and int primitive data types
Character, Byte, Short, and Integer wrapper types
enumerated types
the String type.

which is out of date as general reference types for switch selector variables are supported as of Java SE 21.

See https://docs.oracle.com/javase/specs/jls/se21/html/jls-14.html#jls-14.11 where it is stated that:

The type of the selector expression must be char, byte, short, int, or a reference type, or a compile-time error occurs.

Image

Finally, as of Java SE 14 in 2020 (not even as recently as SE 21 in 2023),
SwitchLabels can have
multiple comma separated
case constants (not specific to switch rule arrow form nor switch expressions, also supported in old fashioned switch statements with colon),
e.g.
"case 1: case 2:"
can rewritten
"case 1, 2:",
but on "Branching with Switch Statements" https://dev.java/learn/language-basics/switch-statement/ (archived as is at https://web.archive.org/web/20260321191854/https://dev.java/learn/language-basics/switch-statement/ ; note page unchanged since 2026-03-12, so 2026-03-21 forwards to 2026-03-12 snapshot)
there is an example introduced with "The following code example, shows how a statement can have multiple case labels"
which uses the old format:

// [...]

switch (month) {
    case 1: case 3: case 5:   // January March May
    case 7: case 8: case 10:  // July August October
    case 12:
        numDays = 31;
        break;
// [...]

instead of the simpler case 1,3,5,7,8,10,12: that is possible since Java 14.

Note that the "Branching with Switch Expressions" https://dev.java/learn/language-basics/switch-expression/ (archived as is at https://web.archive.org/web/20260321202312/https://dev.java/learn/language-basics/switch-expression/ ) page does show the new multiple case constants per switch label syntax, but it suggests that this only supported for switch expressions and the new switch rule arrow syntax, which is not the case, it is also supported in the switch statements (whether using switch rule with arrows or old fashioned switch block statement group syntax).


In "Branching with Switch Expressions" https://dev.java/learn/language-basics/switch-expression/ (archived as is at https://web.archive.org/web/20260321202312/https://dev.java/learn/language-basics/switch-expression/ )
it is mentioned that

The cases of a switch expression must be exhaustive. For all possible values, there must be a matching switch label.

If/when this page is also updated to mention the new pattern matching support for switch blocks with case refinement via guards ( https://openjdk.org/jeps/441 ), it may also be worth mentioning (as is emphasized in for example the excellent Enthuware books recommended by dev.java for certification preparation) that the exhaustiveness is limited to that which can be verified at compile time.

jshell> Integer i = 1;
i ==> 1

// recognized as exhaustive
jshell> Integer k = switch (i) { case Integer j -> j;}
k ==> 1

// Integers must be < 0 or >= 0, but we add a default also and it  is recognized as exhaustive:
jshell> Integer k = switch (i) {
  ...>     case Integer j when j >= 0 -> j;
  ...>     case Integer j when j < 0 -> j;
  ...>     default -> 0;
  ...> };
k ==> 1
// Without the default case, the Java compiler would be unable to determine that Integers < 0 and Integers >= 0 covers all cases and is exhaustive, so it is rejected:
jshell> Integer k = switch (i) {
  ...>     case Integer j when j >= 0 -> j;
  ...>     case Integer j when j < 0 -> j;
  ...> };
|  Error:
|  the switch expression does not cover all possible input values
|  Integer k = switch (i) {
|              ^-----------...

// Noting that while a default was required here 
// because the compiler would not be able to see it was already exhaustive,
// in general, adding a default to an already exhaustive set of cases is NOT allowed, when the compiler can detect it:
jshell> Integer k = switch (i) {  
  ...>     case Integer j -> j;
  ...>     default -> 0;
  ...> }
|  Error:
|  switch has both an unconditional pattern and a default label
|      default -> 0;
|      ^-----^

Thanks so much for all the excellent content!
Feel free to close this as a duplicate or move it to a sub-issue if this is already on your roadmap.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions