Pattern matching on Class<T>
Brian Goetz
brian.goetz at oracle.com
Sat Jun 11 14:20:13 UTC 2022
This has been raised before, and it goes in the category of "not a
terrible idea, but much lower priority that a lot of other things."
First, this feature is bigger than you are imagining. Switch has an
ad-hoc set of allowed constant switch labels (integers, strings,
enums). And, if we never thought about pattern matching, adding another
ad-hoc set of constant labels might have seemed a reasonable extension.
But now that we have patterns, it becomes clear that these are only
"switch-deep", because while you might be able to say:
case Foo.class:
you can't use `Foo.class` as a constant pattern in a nested context:
case Bar(Foo.class):
We need to settle our story for constant patterns first. And, that one
is also not at the top of the priority list.
That's the cost side; on the benefit side, the benefit is pretty
marginal. (It's not zero, but its marginal.) If you have an Animal in
hand, you can use a type pattern. (Having two ways to do it, which are
subtly different, also is likely to be a source of bugs.)
And, if you really want to switch on class literals in O(1) time, you
can use a Map. Your example can be written as:
Map<Class<? extends Animal>, String> sounds
= Map.of(entry(Dog.class, "WOOF"),
entry(Cat.class, "MEOW"),
...);
And, if you want behavior, you can put a lambda in the map. So the
power of the feature is pretty limited; its mostly notational. But its
not notational in a way that (currently) synergizes with other
notations, making it weak.
On 6/11/2022 8:06 AM, Vikram Bakshi wrote:
> Hello,
>
> Apologies if code formatting does not appear well (I'm not used to mailing
> lists). All code blocks are available to view here:
> https://stackoverflow.com/questions/72545671/pattern-matching-on-classt
>
> I asked a question on stackoverflow about why we cannot match on Class<T>
> in the way I expect. Suppose we have:
>
> sealed interface Animal permits Dog, Cat, Bear {}
> record Dog() implements Animal {}
> record Cat() implements Animal {}
> record Bear() implements Animal {}
>
> This would work:
>
> static <T extends Animal> String makeNoise(Class<T> cls) {
> if(cls.equals(Dog.class)) {
> return "WOOF";
> } else if(cls.equals(Cat.class)) {
> return "MEOW";
> } else if(cls.equals(Bear.class)) {
> return "ROAR";
> } else {
> throw new IllegalStateException("Unknown state");
> }
> }
>
> But this wouldn't:
>
> static <T extends Animal> String makeNoisePatternMatch(Class<? extends
> Animal> cls) {
> return switch(cls) {
> case Dog.class c -> "WOOF";
> case Cat.class c -> "MEOW";
> case Bear.class c -> "ROAR";
> };
> }
>
> I got the answer on Stack Overflow that this is not allowed by the spec. My
> question to the mailing list is: will the current restriction be lifted in
> the future?
>
> Will we be able to pattern match like the above? And if not - how come it
> is restricted (I'm not criticising but rather just asking more out of
> curiosity to understand the decision to maintain the restriction)?
>
> Regards,
More information about the amber-dev
mailing list