Summary
Regulate the type of the exception emitted if no switch labels applies
at runtime for both sealed hierarchies and enum classes.
An exhaustive switch over an enum class (i.e. a switch
expression, or a
pattern switch
statement) should throw MatchException
rather than
IncompatibleClassChangeError
if no switch label applies at runtime.
Problem
When introducing switch
expressions, we opted for a design where the switch
body had to be exhaustive. When switching over an enum type, a switch body with
case
labels supporting all the enum constants for the enum type is considered
exhaustive, meaning a default
clause is not needed.
However, there is a possibility that the enum class is changed after compilation
of the switch
expression, and a new enum constant added. Then when executing
the switch
expression, no label would apply.
In JDK 14 it was decided that in this case, an IncompatibleClassChangeError
should be thrown as that was a pre-existing exception that was generally
understood by developers as a signal that things have got out of sync and
re-compilation is needed.
With the development of pattern matching enhanced switches, there are similar
examples where a switch that has been determined to be exhaustive may, at
runtime, find that case
labels apply because of a change to a type hierarchy.
For example, a switch over a sealed
type: When switching over a sealed type, a
switch body with case
labels with type patterns matching all the permitted
subclasses is considered exhaustive, meaning a default clause is not needed.
If the sealed
hierarchy has been changed after compilation of the switch, it
is possible that when executing the switch that no label would apply. In this
case we have settled on throwing a new exception MatchException
.
Unfortunately, currently we have a situation where a pattern switch could throw one of two possible exceptions depending on the type of the selector expression. In the future we plan on allowing switches with patterns that also contain enum constants. In this situation, it will be very difficult to define a runtime semantics that behaves in a predictable way. This will make many future enhancements confusing at best, or impossible at worst.
Solution
The solution is fairly straightforward: an exhaustive switch over an enum class
(i.e. a switch
expression, or a pattern switch
statement) should throw
MatchException
rather than IncompatibleClassChangeError
if no switch label
applies at runtime.
This changed behavior will currently only be selected when preview features are enabled with --enable-preview
, as part of the pattern matching for switch feature. When the pattern matching for switch feature will be finalized, this behavior will become a standard behavior, but will only be used for up-to-date source level. I.e. behavior for older source levels will remain unchanged.
This has been discussed in the expert group and has broad support: https://mail.openjdk.org/pipermail/amber-spec-experts/2022-November/003650.html
Alternatives:
Throw
IncompatibleClassChangeError
for all pattern matching failures. This feels like the wrong solution as firstly it could mean that pattern matching failures will be inadvertently caught by the wrong code. Moreover,IncompatibleClassChangeError
has an accepted meaning to developers that doesn't match these erroneous cases.Make
MatchException
a subtype ofIncompatibleClassChangeError
. This will just add further confusion to developers.Keep the two exceptions and not develop the further enhancements to pattern matching. This would stop an important thread of future enhancements planned under Project Amber.
Specification
Attached is the revised JLS spec change document, and also a diff showing the changes to the spec to support these changes.
- csr of
-
JDK-8297118 Change IncompatibleClassChangeError to MatchException for exhaustive switch statements and switch expressions
- Resolved