---
5.5: There's this "downcast compatible" notion that was apparently introduced
with instanceof pattern matching. It means a cast is legal and not unchecked. Ok. But—not
all legal casts are downcasts, right? It's pretty confusing to use a term that suggests
that they are.
I tripped over this a few times as well, until I just memorized what
"DC" meant. A better name is welcome.
6.3.3.1: Maybe someday we'll have expressions nested in patterns, but for now
isn't it meaningless to ask whether a pattern variable is in scope within
another part of the same pattern?
This may be leftover from when we had guarded patterns `P && e`, where
`e` was part of the pattern?
- It's weird that 'null' is the one case constant that can't come after a 'default',
given that 'null' is the one value that can't be handled by a 'default'. :-) Since we
have to allow other case constants after 'default' anyway, I think it's best to allow
'null' as one more. ("default dominates everything except constants" is a rule
I can remember.)
I suspect that this can be simplified further, and will have to be
anyway when we do underscore (since we will be allowing multiple
patterns on a single case.)
14.11.1: I don't think we prevent this scenario:
switch (obj) { case String s: case Object o: }
The rules about duplicate pattern labels apply to "a statement is labeled...".
There's no labeled statement here.
I think we should probably prohibit this, for the same code hygiene reason that
we prohibit it before a statement group that doesn't try to use any of the
variables.
(This would not be a legal switch expression, because switch expressions can't
fall out. But enhanced switch statements can, as long as they're exhaustive.)
Let's be clear about why we'd be prohibiting this. Arguably, this could
be OK as long as there is no use of either `s` or `o` here. But that
seems pretty useless.
The basis for disallowing should be that you can't fall into, or out of,
a case label that declares a pattern variable.
14.11.2: Design suggestion: rename "enhanced switch" to "pattern switch", define it as
only those switches that make use of patterns, and don't worry about the remaining "switch with new
features" corner cases. It's just such an important concept that I think there's a benefit to making the
distinction really clean and obvious. E.g., asking someone new to Java to memorize the ad hoc set of types
that don't demand exhaustiveness seems unhelpfully complicated.
(Corner cases I'm thinking about: want to use a null constant but not be
exhaustive? Fine. Want to have an Object input type but an empty switch body?
Pointless, but fine. Etc.)
I think the motivation here is that we want to minimize the surface area
of non-exhaustive switches, by quarantining the "need not be exhaustive"
to those that would have compiled under Java 8. "Switches must be
exhaustive, except for statement switches over T1..Tn with all constant
labels."
14.11.3, 15.28.2: Opinionated take: we're continuing to throw ICCE when an
unmatched enum constant slips through a switch, because it would be a
(behaviorally) incompatible change to throw something else. Meanwhile,
unmatched sealed classes get a MatchException. I think there are a tiny number
of people who would notice if we changed from ICCE to MatchException for enums
too, and a lot more people who will have to cope with the historical baggage
going forward if we don't. We should just standardize on MatchException.
Probably better, it's a small incompatibility. No one could be relying
on the ICCE...
14.14.2: I'm sure there's been some discussion about this already, but the use
of MatchException for nulls in 'for' loops but NPE for nulls in switches also
seems like a sad historical wart. What's wrong with an NPE from a 'for' loop?
I think if the RHS of the foreach loop is null, we should NPE (as
before.) But if one of the _elements_ of the RHS array/iterable is
null, then we should ME on the record pattern. (Otherwise we have a
sharp edge between a top-level record pattern and a nested record pattern.)
14.30.1: Parenthesized patterns are no fun for a spec writer. :-) Are they
actually useful? I'm not sure I've seen an example demonstrating what they're
for. (The JEP only talks about them abstractly.)
They were added when we were doing guarded patterns and never took them
out. They will be useful again when we have & and | for patterns; right
now they're just taking up space.
14.30.1, 14.30.2: I'm not sold on *any patterns*, *resolved patterns*, and
*executable switch blocks*.
The semantics are fine—some type patterns will match null, others will not. But it seems
to me that this can be a property of the type pattern, one of many properties of programs
that we determine at compile time. No need to frame it as rewriting a program into
something else. (Compare the handling of the '+' operator. We don't rewrite to a
non-denotable "concatenation expression".)
Concretely:
- The pattern matching runtime rules can just say "the null reference matches a type
pattern if the type pattern is unconditional".
- We can make it a little more clear that a type pattern is determined to be
unconditional, or not, based on its context-dependent match type (is that what
we call it?)
For a *compiler*, it will be useful to come up with an encoding that preserves the
compile-time "unconditional" property in bytecode. But that's a compiler
problem, not something JLS needs to comment on.
It is a property of the pattern *and the type being matched*. We tried
writing it the other way and it was not necessarily better....
14.30.3: A record pattern can't match null, but for the purpose of dominance,
it's not clear to me why a record pattern can't be considered unconditional,
and thus dominate the equivalent type pattern or a 'default'.
Unconditional means "no runtime checks"; it's like a static cast for
which we don't emit a checkcast. Exhaustive means "satisfies the type
system, but might have bad values" (null, novel enum constants, novel
subtypes, at any level in the tree.)