Is it possible to use Class<?> with the new Pattern-Matching switch?

772 Views Asked by At

As part of an investigation into the parameters of a method, I tried the new Pattern Matching for switch (Preview). Using a traditional condition, it works perfectly:

Method firstMethod = BitSet.class.getDeclaredMethods()[0];
Parameter firstParameter = firstMethod.getParameters()[0];
if (firstParameter.getType() == Integer.class) {
        System.out.println("Integer");
    }

When I tried to refactor it to use a switch statement, it did not compile:

Method firstMethod = BitSet.class.getDeclaredMethods()[0];
Parameter firstParameter = firstMethod.getParameters()[0];
switch (firstParameter.getType()) {
    case Integer.class: System.out.println("Integer");
    case int.class: System.out.println("int");
    default: System.out.println("other");
}

The error is:

 error: incompatible types: Class<Integer> cannot be converted to Class<CAP#1>
        case Integer.class: System.out.println("Integer");
                    ^
 where CAP#1 is a fresh type-variable:
   CAP#1 extends Object from capture of ?

Is this something that can't be done, or is it just a syntax error?

1

There are 1 best solutions below

6
Holger On BEST ANSWER

This is not how switching over types work. You can switch over an object’s actual type and have to specify type names, rather than Class literals.

Object o = 42;
switch(o) {
    case Integer i: System.out.println("Integer " + i); break;
    case String s: System.out.println("String " + s); break;
    default: System.out.println("other");
}

Note that with pattern matching, there is no fall-through support, so specifying break is mandatory. Or you use the new syntax which has no fall-through in the first place

Object o = 42;
switch(o) {
    case Integer i -> System.out.println("Integer " + i);
    case String s -> System.out.println("String " + s);
    default -> System.out.println("other");
}

The object returned by getType() is always an instance of java.lang.Class, so branching by its type makes no sense. This doesn’t mean that comparing the actual values with a switch statement or expression was impossible. The comparison can be performed with a guarded pattern:

Method firstMethodWithParam = Arrays.stream(BitSet.class.getDeclaredMethods())
        .filter(m -> m.getParameterCount() > 0)
        .findAny().orElseThrow();
switch(firstMethodWithParam.getParameterTypes()[0]) {
    case Class<?> cl when cl == Integer.class -> System.out.println("Integer");
    case Class<?> cl when cl == int.class -> System.out.println("int");
    case Class<?> cl when cl == String.class -> System.out.println("String");
    case Class<?> cl when cl == long.class -> System.out.println("long");
    case Class<?> cl when BitSet.class.isAssignableFrom(cl)
                                  -> System.out.println("BitSet or subtype");
    default -> System.out.println("other");
}

Note that the JDK 17 preview used && instead of when

but that’s only for completeness. I think it’s obvious that this is no improvement over if statements or a map from Class to handler.