Summary
Enhance the Java language with pattern matching for switch
statements and switch
expressions.
This allows patterns to appear in case
labels, and uses pattern matching to determine which
case
label matches the selector expression. A new kind of case
label, case null
, is introduced,
such that a switch
with case null
does not automatically throw NullPointerException
when the
selector expression is null
. The meaning of existing switch
statements and switch
expressions,
without case null
, is unchanged. Pattern matching for switch
complements pattern matching for
instanceof
, as finalized in Java 16.
Problem
We often want to compare an variable against multiple alternatives. Java
supports multi-way comparisons with switch
statements and, since Java 14,
switch
expressions, but unfortunately switch
is very limited. You can only
switch on values of a few types — numeric types, enum types, and String
— and
you can only test for exact equality against constants. We might like to use
patterns to test the same variable against a number of possibilities, taking a
specific action on each, but since the existing switch
does not support that,
we end up with a chain of if
...else
tests such as:
static String formatter(Object o) {
String formatted = "unknown";
if (o instanceof Integer i) {
formatted = String.format("int %d", i);
} else if (o instanceof Long l) {
formatted = String.format("long %d", l);
} else if (o instanceof Double d) {
formatted = String.format("double %f", d);
} else if (o instanceof String s) {
formatted = String.format("String %s", s);
}
return formatted;
}
This code benefits from pattern matching for instanceof
expressions, but it is far from
perfect. First and foremost, this approach allows coding errors to remain hidden
because we have used an overly general control construct. The intent is to
assign something to formatted
in each arm of the if
...else
chain, but there is
nothing that enables the compiler to identify and verify this invariant. If some
block — perhaps one that is executed rarely — does not assign to formatted
, we
have a bug. In addition, the above code is not optimizable; absent
compiler heroics it will have O(n) time complexity, even though the underlying
problem is often O(1).
If we extend switch
statements and switch
expressions to work on any type,
and allow case
labels with patterns rather than just constants, then we could
rewrite the above code more clearly and reliably:
static String formatterPatternSwitch(Object o) {
return switch (o) {
case Integer i -> String.format("int %d", i);
case Long l -> String.format("long %d", l);
case Double d -> String.format("double %f", d);
case String s -> String.format("String %s", s);
default -> o.toString();
};
}
The intent of this code is clearer because we are using the right control
construct: we are saying, "the parameter o
matches at most one of the following
conditions, so figure it out and evaluate the corresponding arm." As a bonus, this code
is optimizable; we are likely to be able to perform the dispatch in O(1) time.
Solution
The Java language is enhanced as follows, in both switch
statements and switch
expressions:
Allow patterns as
case
labels for switch blocks.Allow
null
as acase
label for switch blocks.Introduce a new semantics for
switch
that uses pattern matching to select a matching switch label when the switch label uses a pattern.The semantics of
switch
become "null
-sensitive": if the value of the selector expression isnull
and the switch block contains anull
label, then this label is said to match. If the value of the selector expression isnull
and the switch block does not contain thenull
label, thenNullPointerException
is thrown as usual. This is a conservative extension to the semantics ofswitch
: all existingswitch
statements andswitch
expressions will continue to execute as they do in Java 16.Allow the selector expression of a
switch
statement orswitch
expression to be of any type, not just the traditional set ofchar
,byte
,short
,int
,Character
,Byte
,Short
,Integer
,String
, or enum type. This change is conservative in that an existingswitch
statement orswitch
expression that usescase
labels with constants will continue to require that the type of the selector expression be one ofchar
,byte
,short
,int
,Character
,Byte
,Short
,Integer
,String
, or an enum type.Introduce the notion of dominance for switch labels, to prevent switch blocks containing switch labels that are "shadowed" by other labels appearing earlier in the block. For example:
switch (x) { case Object o -> ... case String s -> ... }
This switch block is erroneous because any value that matches
String s
will always matchObject o
-- we say the switch labelcase Object o
dominates the switch labelcase String s
. Dominance is determined with regards to the order that switch labels appear in the switch block. This means that the followingswitch
statement is permitted:switch (x) { case String s -> ... case Object o -> ... }
Here, a string value for
x
will match the first switch labelcase String s
, and any other non-null
value will match the second switch labelcase Object o
.Enhance the existing notion of completeness for
switch
expressions to deal with pattern labels by employing a new concept of pattern totality.switch
expressions continue to be restricted so it is not possible for no switch label to match at runtime.Introduce a new kind of pattern, guarded patterns, as a means of refining a pattern with a boolean expression. For example, a value matches the guarded pattern
List l && l.length()>2
if it first matches the type patternList l
, and (only if it matches) it additionally contains at least two elements.Introduce a new kind of pattern, parenthesized patterns, to help address some parsing issues with guarded patterns containing nested && expressions
The standard libraries will be enhanced with java.lang.runtime.SwitchBootstrap
(a preview class) with a bootstrap method (`typeSwitch), that will
categorize switch selector values to case numbers.
javac
will be enhanced to parse the enhanced Java language, and its Trees API
will be enhanced to cover it.
Specification
Update on June 4: the specification for the bootstrap method has been changed as per review feedback. The updated full specdiff is attached as specdiff.01-4.zip, diff from the previously approved version is attached as specdiff.00-01-4.zip. These are also available for convenience here, respectivelly:
- http://cr.openjdk.java.net/~jlahoda/8262891/specdiff.01/overview-summary.html
- http://cr.openjdk.java.net/~jlahoda/8262891/specdiff.00-01/overview-summary.html
Original text: The current draft of the specification is attached here as jep406-20210519-2.zip.
The specdiff of the proposed API changes is attached as specdiff-20210519.zip and is also available here for convenience: http://cr.openjdk.java.net/~jlahoda/8262891/specdiff.00/overview-summary.html
- csr of
-
JDK-8262891 Compiler implementation for Pattern Matching for switch (Preview)
- Resolved
- relates to
-
JDK-8275408 Pattern Matching for switch (Second Preview)
- Closed