我有这个:
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”变成只读,没有写许可?
如何理解此编译错误?非常感谢。
答案 0 :(得分:3)
List<?>
表示它是一个列表,但是在编译时我们不知道其中包含什么。
尽管在这种情况下我们可以证明它是List<String>
,但大多数时候我们都不知道它是List<String>
还是List<Integer>
还是{{ 1}}。当然,将List<Animal>
添加到Integer
是相当危险的,因此Java会在编译时停止它。
答案 1 :(得分:1)
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.
答案 2 :(得分:1)
类型转换时没有“只读”。您唯一要做的就是更改泛型类型,这意味着编译器将抱怨差异。如果我们分解代码,由于编译器的解释,您将得到两个不同的结果。
第一:
List<?> list1 = list;
您所做的是将List<String>
下放到List<?>
类型的通配符中。这意味着任何事物都将引发编译错误,仅仅是因为它不知道通配符应该/将是什么。 (这就是为什么添加String会导致编译错误)。
在第二个示例中,您使用了“原始类型”:
List list2 = list;
原始类型不关心泛型类型,在这种情况下,编译器也是如此。这就是为什么没有抛出异常的原因,因为每个类型参数仅默认为Object
(这是Java中每个类的基类)。因此,添加一个也是对象的整数(123
)对于编译器来说是完全合法的。但是在运行时,由于类型不匹配,将引发异常。出于这个确切的原因,使用原始类型被认为是一种不好的做法,因为这些类型的问题可能很难找到/解决。
答案 3 :(得分:1)
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子类型的Car或Toyoto或Ford很好。