Java 17 null case with pattern matching
Asked Answered
C

3

11

I have a Java 17 project using Eclipse 2022-03 and OpenJDK 17:

openjdk 17.0.2 2022-01-18
OpenJDK Runtime Environment Temurin-17.0.2+8 (build 17.0.2+8)
OpenJDK 64-Bit Server VM Temurin-17.0.2+8 (build 17.0.2+8, mixed mode, sharing)

I'm trying to use the new Java 17 switch features, so I tried this in a method:

return switch(testEnum) {
  case foo -> newFoo();
  case bar -> newBar();
}

That works fine. But then I tried this (because the value might be null):

return switch(testEnum) {
  case foo -> newFoo();
  case bar -> newBar();
  case null -> newDefault();
}

Eclipse underlines null in red and says:

Pattern Matching in Switch is a preview feature and disabled by default. Use --enable-preview to enable

Compiling via Maven produces:

[ERROR] /project/src/main/java/com/example/FooBar.java:[432,38] null in switch cases is a preview feature and is disabled by default.
[ERROR]   (use --enable-preview to enable null in switch cases)

My Maven project has:

<properties>
  <maven.compiler.release>17</maven.compiler.release>
</properties>

I know the compiler release setting isn't being ignored; otherwise it would be defaulting to Java 8 (as per my parent POM) and not allowing pattern matching at all.

Isn't null cases with pattern matching part of Java 17? What am I doing wrong?

Crowder answered 13/6, 2022 at 0:25 Comment(6)
What does javac say when you compile that file?Nimwegen
Good idea. I tried with Maven (which should be equivalent to javac), and it gave the same error! I updated the question with this additional information.Crowder
"Isn't null cases with pattern matching part of Java 17? What am I doing wrong?" - It is a preview feature, not a full feature. See stackoverflow.com/questions/52232681 for how to enable preview features in Maven builds.Scalariform
I had read medium.com/@javatechie/… , which seemed to indicate that the null cases was present in Java 17, but I guess that is different than pattern matching. So pattern matching is a full feature in Java 17, and null case statements is a full feature in Java 17, but pattern matching with null is only a preview feature in Java 17? That's sort of confusing.Crowder
In Java 17, Pattern Matching for switch is a preview feature. See new features of Java 17: openjdk.java.net/projects/jdk/17Soddy
I just noted that enabling preview features under Eclipse isn’t helpful either. It compiles, but produces a NullPointerException at runtime. Seems, that support isn’t complete.Unbated
N
21

Update: Pattern matching for switch arrived in Java 21.

tl;dr

👉 switch expressions != pattern matching with switch

You are mixing up the relatively new feature of switch expressions with the still-not-released feature of pattern matching with switch.

The switch feature in Java has been evolving through 3 phases, two completed:

  • ☑️ switch statement (original feature in Java 1)
  • ☑️ switch expression (Java 14+)
  • ❌ Pattern matching for switch, including case null (previewed in Java 17, 18, 19, & 20)

Do not conflate switch expressions with pattern matching

Classic switch

Understand that historically, the Java switch statement has been hostile to null checks. See this Question, How to use null in switch. As shown there, code such as this:

switch (i) {
    case null:
        doSomething0();
        break;    
}

… was not possible.

switch expressions

Fast forward to Java 14, when switch expressions was added to Java. See JEP 361: Switch Expressions. That allows the syntax seen in your first code example:

return switch(testEnum) {
  case foo -> newFoo();
  case bar -> newBar();
}

But read the JEP. No mention of null — « crickets ».

Pattern matching for switch

Fast forward further, to JEP 406: Pattern Matching for switch (Preview). Note that this is a preview feature in Java 17, not a final, officially released feature. (To understand how preview features work, read JEP 12: Preview Features.)

👉 In that JEP 406, notice its second goal: Allow the historical null-hostility of switch to be relaxed when desired.

Now search that page for “null” — 73 hits! That page explains the former policy of the Java language:

Traditionally, switch statements and expressions throw NullPointerException if the selector expression evaluates to null

Notice the mention of statements, the original switch syntax, and additionally expressions, the new syntax used in your code. In both cases, null check was forbidden.

That page goes on to explain the changes that motivate the inclusion of support for null checks. Read the JEP for will-written details.

👉 The upshot is that you can use case null in a switch in Java 17 — but only if you go out of your way to enable the preview feature.

Still in preview, in Java 20

"We will sell no wine before its time."

There too, in Java 20, you will have to go out of your way to activate the preview feature.

And of course two reminders:

  • Preview features should not be used in production for critical apps.
  • Preview features are likely to change. That is their purpose, to gather feedback and suggestions leading to changes.

Example

Let's try this code example. Notice how we use plain old syntax here, without the ->. The arrow operator is not related to our discussion here.

String x = null;
switch ( x )
{
    case "starburst":
        System.out.println( "Is starburst." );
        break;
    case null:
        System.out.println( "Whoops, null." );
        break;
    default:
        System.out.println( "Some other value found." );
        break;
}

In Java 17, by default, without enabling preview features we get this error while editing the code for case null:

Patterns in switch are not supported at language level '17'

Set language level to 17 (Preview) - Pattern matching for switch

After enabling preview features in Java 17, the code compiles without complaint. And it runs:

Whoops, null.

Nonentity answered 13/6, 2022 at 2:3 Comment(2)
Actually, switch expression do not have to use arrow labels. And a switch statement can now use arrow labels. Those two features are orthogonal - although I admit that they work very well together.Nimwegen
@JohannesKuhn I added an example at the end showing the old-school syntax without arrow label. Thank you.Nonentity
C
1

This command line works for me:

$ javac -version
javac 17.0.3
$ javac  -source 17 -Xlint:preview --enable-preview org/kablambda/Main.java
org/kablambda/Main.java:10: warning: [preview] null in switch cases is a preview feature and may be removed in a future release.
            case null -> "c";
                 ^
1 warning
Crystalcrystalline answered 13/6, 2022 at 1:24 Comment(0)
D
-1
return (null == testEnum)
    ? newDefault()
    : switch (testEnum) {
      case foo -> newFoo();
      case bar -> newBar();
    };
Detent answered 24/4 at 19:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.