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