62. Understanding the unconditional patterns and nulls in switch expressions
Let’s imagine that we use JDK 17 and we have the following code:
private static String drive(Vehicle v) {
return switch (v) {
case Truck truck -> “truck: ” + truck;
case Van van -> “van: ” + van;
case Vehicle vehicle -> “vehicle: ” + vehicle.start();
};
}
drive(null);
Notice the call, drive(null). This call will hit the Vehicle vehicle total pattern, so even null values match total patterns. But, this means that the binding variable vehicle will be null also, which means that this branch is prone to NullPointerException (for instance, if we call a hypothetical method, vehicle.start()):
Exception in thread “main” java.lang.NullPointerException: Cannot invoke “modern.challenge.Vehicle.start()” because “vehicle” is null
Because Vehicle vehicle matches all possible values is known as a total pattern but also as an unconditional pattern since it matches everything unconditionally.But, as we know from Problem 49, starting with JDK 17+ (JEP 427) we can have a pattern label for null itself, so we can handle the previous shortcoming as follows:
return switch (v) {
case Truck truck -> “truck: ” + truck;
case Van van -> “van: ” + van;
case null -> “so, you don’t have a vehicle?”;
case Vehicle vehicle -> “vehicle: ” + vehicle.start();
};
Yes, everybody agrees that adding a case null between vehicles looks awkward. Adding it at the end is not an option since will raise a dominance issue. So, starting with JDK 19+, adding this case null is no longer needed in this kind of scenario. Basically, the idea remains the same meaning that the unconditional pattern still matches null values only that it will not allow the execution of that branch. Actually, when a null value occurs, the switch expressions will throw a NullPointerException without even looking to the patterns. So, in JDK 19+, this code will throw an NPE right away:
return switch (v) {
case Truck truck -> “truck: ” + truck;
case Van van -> “van: ” + van;
// we can still use a null check
// case null -> “so, you don’t have a vehicle?”;
// total/unconditional pattern throw NPE immediately
case Vehicle vehicle -> “vehicle: ” + vehicle.start();
};
The NPE message reveals that vehicle.start() was never called. The NPE occurred much earlier:
Exception in thread “main” java.lang.NullPointerException at java.base/java.util.Objects.requireNonNull(Objects.java:233)
We will expand on this topic later when we will talk about Java records.
Summary
That’s all folks! This was a comprehensive chapter that covered among others four main topics: java.util.Objects, immutability, switch expressions, and pattern matching for instanceof and switch expressions.