Java 通用版本 8 与 17 的实例类型

Java instance type with generic version 8 vs 17

根据 Maurice Naftalin 的 Java Generic 和 Collections,在以下选项中 -

  1. obj instanceof List
  2. obj instanceof List
  3. obj instanceof List

第 1 和第 2 是允许的,但第 3 个不是,这在 java 8(版本“1.8.0_321”)中工作正常。选项 1 和 2 编译,选项 3 给出编译错误。 但是第三个选项适用于 java 17(版本“17.0.1”2021-10-19 LTS)。你能帮我理解为什么它在 java 17 中工作吗? 我正在尝试的示例代码 -

var a = List.of(2,3,4,5,23);
var b = a instanceof List<? extends Object>;
return b;

在 Java16 之前,instanceof 的唯一目的是检查引用的对象是否可分配给指定的类型,因为类型擦除会阻止检查对象是否真正可分配给一个参数化类型,允许一个假装这样的测试是可能的表达式是没有意义的。

唯一可以测试的是对象是否是 List 的实例,因此,它只允许写 … instanceof List… instanceof List<?>,因为两者都不假装测试列表的元素类型。

从 Java16(自 14 起可作为预览版)开始,instanceof 允许声明指定类型的新变量。

例如:

public void someMethod(Iterable<String> i) {
    if(i instanceof List<String> l) {
        if(l.isEmpty()) return;
        // optimized List processing
    } else {
        // generic iterable processing
    }
}

相当于

public void someMethod(Iterable<String> i) {
    if(i instanceof List<String>) {
        List<String> l = (List<String>)i;
        if(l.isEmpty()) return;
        // optimized List processing
    } else {
        // generic iterable processing
    }
}

对于新变量的声明,限制为原始类型或通配符类型是不切实际的。因此,现在允许指定与隐含变量声明和赋值相关的实际类型参数。由于仍然无法检查元素类型,因此您只能指定 compile-time 源类型可以在没有 unchecked 强制转换的情况下转换为的类型。

因此,以下内容无效

Iterable<?> i = null;
boolean b = i instanceof List<String>;

因为你不能安全地从 Iterable<?> 转换为 List<String>

在您的示例中,var a = List.of(2,3,4,5,23);a 声明为 List<Integer>,因此,a instanceof List<? extends Object> 有效,因为 List<Integer> 可分配给 List<? extends Object> (即使没有演员表)。

当然,指定未检查的元素类型仅在您像第一个示例中那样声明变量时才有用。但是 Java 语言遵循不添加额外规则的原则,只是因为某些结构不如其他结构有用。因此,无论是否声明变量,您现在始终可以按照相同的规则为 instanceof 指定实际类型参数。

此功能称为 Pattern Matching,因为它被认为是 Java 所追求的更广泛概念的特例。在 JDK 17 中,switch 有一个类似的预览功能。

另请参阅 Java 语言规范中的 §14.30. Patterns