Primitive type-test patterns
Given that patterns include constant expressions, and type tests possibly
including generic types; it seems reasonable to consider the possibility of
allowing primitive type tests in pattern matching. (This answers a
sometimes-requested feature: can instanceof support primitive types?)
However, it is not wholly obvious what this test might mean. One possibility is
that a “type-restating” equivalent for primitive type-test patterns is
assignment conversion; e.g. if I have
case int x:
then a target whose static type is byte, short, char, or int – or their boxes –
will be statically deemed to match.
A target whose dynamic type can be assigned to the primitive type through a
combination of unboxing and widening (again, assignment conversion) matches a
primitive type test. So if we have:
switch (o) {
case int i: ...
we have to do instanceof tests against {Integer,Short,Character,Boolean} to
determine a match.
A primitive type test pattern dominates other primitive type patterns according
to assingment compatibility; int dominates byte/short/char, long dominates
int/byte/short/char, and double dominates float.
A primitive type test pattern is inapplicable (dead) if cast conversion from
the static type of the target fails:
Map m;
switch (m) {
case int x: // compile error
}
The dominance interaction between primitive type-tests and reference type-tests
for the wrapper types (and their supertypes) seems messy. Consider the
following combinations:
case int n:
case Integer n: // dead
case Integer n:
case int n: // not dead -- still matches Short, Byte
case Byte b:
case byte b: // dead
case Number n:
case int n: // dead
Is there some unifying theory that makes sense here? One possibility is to take
a more denotational view: a type is a set of values, so type restatement is
really about semantic set inclusion, and dynamic testing is about set
membership. Is this adding too much complexity? Do developers really care about
this feature?