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
IncompatibleClassChangeErrorfor 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,IncompatibleClassChangeErrorhas an accepted meaning to developers that doesn't match these erroneous cases. -
Make
MatchExceptiona 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
-