4

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?

yoni
  • 1,164
  • 2
  • 13
  • 29
  • This does not look like the new syntax, you probably want something like `case Class ci : System.out.println("Integer"); break;` and similar for `int`/ – Holt Dec 01 '21 at 09:45
  • It also doesn't work – yoni Dec 01 '21 at 10:23
  • 1
    The new switch expression branches the object by its class. `firstParameter.getType()` returns a class object (an instance of `Class`), so that class is always `Class`. So you can't branch in your example. –  Dec 01 '21 at 10:51

1 Answers1

8

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 && cl == Integer.class -> System.out.println("Integer");
    case Class<?> cl && cl == int.class -> System.out.println("int");
    case Class<?> cl && cl == String.class -> System.out.println("String");
    case Class<?> cl && cl == long.class -> System.out.println("long");
    case Class<?> cl && BitSet.class.isAssignableFrom(cl)
                                  -> System.out.println("BitSet or subtype");
    default -> System.out.println("other");
}

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.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • 5
    Note too there is a difference between `x instanceof C` and `x.getClass() == C.class`; the former includes C and its subtypes; the latter includes exactly C only (which might never be true if C is abstract.) Type patterns are like `instanceof C`; for final classes like `Integer` it makes no difference, but that's the exception, not the rule. – Brian Goetz Dec 01 '21 at 17:27
  • 3
    @BrianGoetz good point. I expanded the example to show how supporting subtypes would look like. – Holger Dec 01 '21 at 17:36
  • 2
    For completeness, the reflective spelling of `instanceof` is `Class::isAssignableFrom`. – Brian Goetz Dec 01 '21 at 18:12
  • 1
    Well, yes, that’s what I added in the example. – Holger Dec 01 '21 at 18:23