Java通用通配符捕获&引用;限制禁止修改值

Java通用通配符捕获&引用;限制禁止修改值,java,list,generics,wildcard,add,Java,List,Generics,Wildcard,Add,我有这个: 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(1

我有这个:

    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 List=new ArrayList();
列表。添加(“确定”);
列表。添加(“123”);
列表1=列表;//不允许修改的目的是什么?
列表1.添加(“xyz”)//编译错误
列表2=列表;
清单2.添加(123)//运行时异常
奇怪的是:

List<?> list1 = list; // What is the point of disallow modify?
list1.add("xyz");//compile error
List list1=List;//不允许修改的目的是什么?
列表1.添加(“xyz”)//编译错误
为什么它不允许我写一个元素,甚至类型匹配?我想是不是在没有写权限的情况下,将“list1”设置为只读

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

列表
表示它是一个列表,但在编译时我们不知道其中包含什么

虽然我们可以在这种情况下证明它是一个
列表
,但大多数情况下我们不知道它是一个
列表
,还是一个
列表
,还是一个
列表
。当然,向
列表添加
整数
相当危险,因此Java会在编译时停止它。

列表
意味着
列表
包含未知类型,在运行时它可以是任何类型。因此,您不能在编译阶段为其分配
列表
,它不是类型安全的

假设允许您向其中添加元素:

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.
List List=new ArrayList();
列表1=列表;
列表1.添加(“123”);//列表添加到和字符串“123”是可以的,但列表1添加字符串“123”不是类型安全的,因为它在运行时包含未知类型。

在进行类型转换时没有“只读”。您所做的唯一一件事就是更改泛型类型,这意味着编译器将抱怨差异。如果我们分解代码,由于编译器的解释,您会得到两个不同的结果

第一:

List<?> list1 = list;
原始类型不关心泛型类型,在这种情况下,编译器也不关心泛型类型。这就是为什么没有抛出异常,因为每个类型化参数都默认为
对象
(java中每个类的基类)。因此,对编译器来说,添加一个整数(
123
)也是一个对象是完全合法的。但是,在运行时会引发异常,因为类型不匹配。正因为如此,使用原始类型被认为是一种不好的做法,因为这类问题可能很难找到/解决

列表
只能用作参考声明。这是告诉编译器它可以保存任何类型的列表的一种方式,但我不会向列表中添加任何内容

public void printMyList(List<?> mylist){
  mylist.forEach(item->System.out.println(item)); // works fine
  mylist.add("abcd"); //compilation error
}
public void printMyList(列表mylist){
mylist.forEach(item->System.out.println(item));//工作正常
mylist.add(“abcd”);//编译错误
}
编译器会阻止这种情况发生,因为您可能会在列表中输入错误的类型

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

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

public void printMyList(列表)您不应该使用原始类型。它们只是为了向后兼容。您可以将
null
添加到
列表中。
public void printMyList(List<? super Car> mylist){
  mylist.add(new Toyota()); //works fine
}