Java通用通配符捕获“?”限制禁止修改值

时间:2018-12-08 08:01:47

标签: 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(123);//runtime exception

奇怪的是:

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

为什么即使类型匹配也不允许我写元素?我想让“ list1”变成只读,没有写许可?

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

4 个答案:

答案 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很好。