-
Enhancement
-
Resolution: Fixed
-
P4
-
None
-
b26
In Java 19, JEP 427 "Pattern Matching for switch (Third Preview)" adds a new null case label. When it appears alongside a type pattern, not only should the case label apply when the value of the selector expression is null but the pattern variable should be initialised with null.
This is handled by resolving the switch label to use ANY patterns. The specification states that:
case null, String s
is resolved to
case ANY(String s)
So it will match when the selector expression is the null reference. But this is incorrect as the label `case String s` can also be compiled to `case ANY(String s)` (when the selector expression is of type String) and this should not match when the selector expression is the null reference.
The correct resolving of the label is:
case null, ANY(String s)
and then the semantics of switch should look for a null constant and then match the pattern also supported by the label. (This will then have the side-effect of initialising the String s pattern variable.)
In addition, the semantics of pattern matching for ANY patterns needs correction. The specification states that any value matches an any pattern. But this incorrectly means that a switch such as
switch(o) {
case null, String s -> ...
case Object o -> ...
}
With the proposed rules for resolving these patterns, this switch is resolved to:
switch(o) {
case null, ANY(String s) -> ...
case ANY(Object o) -> ...
}
But if o is the result of expression `new Object()`, it should be the second label that matches. The rules for pattern matching with an any pattern should read:
- The null reference *matches* an any pattern.
The pattern variable declared by the any pattern is initialized to the null reference.
- A value *v* that is not the null reference *matches* an any pattern of type
*T* if *v* can be cast to *T* without raising a `ClassCastException`; and
*does not match* otherwise.
If *v* matches, then the pattern variable declared by the any pattern is
initialized to *v*.
If *v* does not match, then the pattern variable declared by the any
pattern is not initialized.
This is handled by resolving the switch label to use ANY patterns. The specification states that:
case null, String s
is resolved to
case ANY(String s)
So it will match when the selector expression is the null reference. But this is incorrect as the label `case String s` can also be compiled to `case ANY(String s)` (when the selector expression is of type String) and this should not match when the selector expression is the null reference.
The correct resolving of the label is:
case null, ANY(String s)
and then the semantics of switch should look for a null constant and then match the pattern also supported by the label. (This will then have the side-effect of initialising the String s pattern variable.)
In addition, the semantics of pattern matching for ANY patterns needs correction. The specification states that any value matches an any pattern. But this incorrectly means that a switch such as
switch(o) {
case null, String s -> ...
case Object o -> ...
}
With the proposed rules for resolving these patterns, this switch is resolved to:
switch(o) {
case null, ANY(String s) -> ...
case ANY(Object o) -> ...
}
But if o is the result of expression `new Object()`, it should be the second label that matches. The rules for pattern matching with an any pattern should read:
- The null reference *matches* an any pattern.
The pattern variable declared by the any pattern is initialized to the null reference.
- A value *v* that is not the null reference *matches* an any pattern of type
*T* if *v* can be cast to *T* without raising a `ClassCastException`; and
*does not match* otherwise.
If *v* matches, then the pattern variable declared by the any pattern is
initialized to *v*.
If *v* does not match, then the pattern variable declared by the any
pattern is not initialized.