是否可以将 Class<?> 与新的模式匹配开关一起使用?

yon*_*oni 4 java switch-statement preview-feature java-17

作为对方法参数的调查的一部分,我尝试了新的switch 模式匹配(预览版)。使用传统条件,它可以完美运行:

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

当我尝试重构它以使用switch语句时,它没有编译:

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");
}
Run Code Online (Sandbox Code Playgroud)

错误是:

 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 ?
Run Code Online (Sandbox Code Playgroud)

这是无法完成的事情,还是只是语法错误?

Hol*_*ger 8

这不是类型切换的工作原理。您可以切换 object\xe2\x80\x99s 的实际类型,并且必须指定类型名称,而不是Class文字。

\n
Object o = 42;\nswitch(o) {\n    case Integer i: System.out.println("Integer " + i); break;\n    case String s: System.out.println("String " + s); break;\n    default: System.out.println("other");\n}\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,对于模式匹配,没有失败支持,因此指定break是强制性的。或者你使用新的语法,它一开始就没有失败

\n
Object o = 42;\nswitch(o) {\n    case Integer i -> System.out.println("Integer " + i);\n    case String s -> System.out.println("String " + s);\n    default -> System.out.println("other");\n}\n
Run Code Online (Sandbox Code Playgroud)\n

返回的对象getType()始终是 的实例java.lang.Class,因此按其类型进行分支是没有意义的。这并不意味着将实际值与switch语句或表达式进行比较是不可能的。可以使用保护模式执行比较:

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

请注意,使用 JDK\xc2\xa017 预览版&&而不是when

\n

但 \xe2\x80\x99s 只是为了完整性。我认为\xe2\x80\x99s 显然这对于​​语句或从处理程序的if映射来说没有任何改进。Class

\n

  • 还要注意 `x instanceof C` 和 `x.getClass() == C.class` 之间是有区别的;前者包括C及其亚型;后者只包含 C(如果 C 是抽象的,这可能永远不会成立)。类型模式​​就像“instanceof C”;对于像“Integer”这样的最终类,这没有什么区别,但这是例外,而不是规则。 (5认同)
  • @BrianGoetz 好点。我扩展了该示例以展示支持子类型的外观。 (3认同)
  • 为了完整起见,“instanceof”的反射拼写为“Class::isAssignableFrom”。 (2认同)
  • @Gili 这个答案是为预览版本编写的。从那时起,语法发生了变化。我会尽快更新答案。 (2认同)