【问题标题】:Java generic wildcard capture "?" restrict forbids modifying a valueJava 通用通配符捕获“?”限制 禁止修改值
【发布时间】:2018-12-08 08:01:46
【问题描述】:

我有这个:

    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”是只读的,没有写权限?

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

【问题讨论】:

  • 您根本不应该使用原始类型。它们只是为了向后兼容。

标签: java list generics wildcard add


【解决方案1】:

List&lt;?&gt; 表示它是一个列表,但我们在编译时不知道其中的内容。

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

【讨论】:

    【解决方案2】:

    List&lt;?&gt; 只能用作引用声明。这是一种告诉编译器它可以保存任何类型的列表但我不会向列表添加任何内容的方式。

    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 或 Toyota 或 Ford 是完全可以的。

    【讨论】:

    • 您可以将null添加到List&lt;?&gt;
    【解决方案3】:

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

    第一:

    List<?> list1 = list;
    

    您所做的是将List&lt;String&gt;“向下转换”为类型为List&lt;?&gt; 的通配符。这意味着任何东西都会引发编译错误,仅仅是因为它不知道通配符应该/将是什么。 (这就是为什么添加字符串会导致编译错误)。

    在您的第二个示例中,您使用了“原始类型”:

    List list2 = list;
    

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

    【讨论】:

      【解决方案4】:

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

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

      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.
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-01
        相关资源
        最近更新 更多