Java 通用通配符捕获“?” restrict 禁止修改一个值

Java generic wildcard capture "?" restrict forbids modifying a value

我知道了:

    List<String> list = new ArrayList<>();
    list.add("ok");
    list.add("123");
    List<?> list1 = list; // What is the point of disallow modify?
    list1.add("xyz");//compile error
    List list2 = list;
    list2.add(123);//runtime exception

奇怪的是:

List<?> list1 = list; // What is the point of disallow modify?
list1.add("xyz");//compile error

为什么它不允许我写一个元素甚至类型匹配?我想 "list1" 只读,没有写权限?

如何理解这个编译错误?非常感谢。

List<?>表示它是一个列表,但我们在编译时并不知道里面有什么。

虽然我们可以在这种情况下证明它是 List<String>,但大多数时候我们不知道它是 List<String> 还是 List<Integer> 还是List<Animal>。当然,将 Integer 添加到 List<String> 是相当危险的,因此 Java 将在编译时停止它。

List<?> 表示 List 包含未知类型,在 运行 时可能是任何类型。所以你不能在编译阶段给它分配一个List<String>,它不是类型安全的。

假设您可以向其中添加元素:

List<String> list = new ArrayList<>();
List<?> list1 = list;
list1.add("123"); // it's ok for an list to and string "123", but it's not type safe for list1 to add string "123", since it contains an unknown type at run time.

类型转换时没有'readonly'。您唯一要做的就是更改泛型类型,这意味着编译器会抱怨差异。如果我们分解代码,由于编译器的解释,您会得到两个不同的结果。

第一个:

List<?> list1 = list;

您所做的是 'down-cast' 一个 List<String> 到通配符类型 List<?>。这意味着任何东西都会抛出编译错误,仅仅是因为它不知道通配符 should/will 是什么。 (这就是为什么添加 String 会导致编译错误)。

在您的第二个示例中,您使用了 'raw-type':

List list2 = list;

原始类型不关心泛型类型,在这种情况下编译器也不关心。这就是为什么没有抛出异常,因为每个类型参数都默认为 Object(这是 java 中每个 class 的基础 class)。因此,添加一个也是对象的整数 (123) 对于编译器来说是完全合法的。但是在运行时抛出异常,因为类型不匹配。出于这个确切的原因,使用原始类型被认为是一种不好的做法,因为这类问题很难 find/solve。

List<?>只能作为参考声明。这是一种告诉编译器它可以容纳任何类型的列表的方式,但我不会向列表添加任何内容。

public void printMyList(List<?> mylist){
  mylist.forEach(item->System.out.println(item)); // works fine
  mylist.add("abcd"); //compilation error
}

编译器会阻止这种情况发生,因为您最终可能会在列表中输入错误的类型。

如果与 super 一起使用,有时允许在通配符的情况下进行修改:

public void printMyList(List<? super Car> mylist){
  mylist.add(new Toyota()); //works fine
}

这告诉编译器接受 Car 的列表类型或 Car 的任何超类型。在这种情况下,添加 Car 或 Toyoto 或 Ford 是 Car 的子类型是完全没问题的。