【问题标题】:Generics Upper Bound Wild Card gives compilation error [duplicate]泛型上限通配符给出编译错误[重复]
【发布时间】:2016-11-20 20:56:56
【问题描述】:

我正在尝试一些关于泛型上限/下限的示例程序。泛型上限给出编译错误...但是下限很好。 我只是想把一个 T 类型的列表放入一个集合中,然后尝试上限和下限场景..

请帮助确定 testUpperBound(T t) 方法的问题,以及为什么 testLowerBound(T t) 方法可以编译而 testUpperBound(T t) 方法不能编译。我检查了其他类似的线程..但我仍然没有弄清楚。

如果需要更多详细信息,请告诉我。

 public class TestGenerics<T> 
    {

        public static void main(String...args)
        {
           List<String> list = new ArrayList<>();
           list.add("New ArrayList");
           new TestGenerics<List<String>>().testUpperBound(list);
           new TestGenerics<List<String>>().testLowerBound(list);

        }

       public  void testLowerBound(T t)
       {
            Set<? super ArrayList<T>> lowerBoundSet = new HashSet<>();
            lowerBoundSet = new HashSet<List<T>>();
            ArrayList<T> list = new ArrayList<>();
            list.add(t);
            lowerBoundSet.add(list);  // compiles..

            out.println(lowerBoundSet);
        }

        public  void testUpperBound(T t)
        {
            Set<? extends List<T>> upperBoundSet = new HashSet<>();
            upperBoundSet = new HashSet<List<T>>();
            ArrayList<T> list = new ArrayList<>();
            list.add(t);
            upperBoundSet.add(list);  // Doesn't compile..

            out.println(upperBoundSet);
        }

    }

【问题讨论】:

    标签: java generics


    【解决方案1】:

    这里有你的答案:

    Explanation of the get-put principle

    这是java规则,仅此而已。 我可能会用你的代码给你一个例子,为什么编译通过会不安全:

    public void extendsExample(){
      Set<? extends List<? extends String>> setOfList = new HashSet<>();
      Set<ArrayList<String>> setOfArrayList = new HashSet<>();
    
      // now setOfList var refers to a set 
      // which contains a arraylist of String
      setOfList = setOfArrayList;
    
      // compilation fails
      setOfList.add(new LinkedList<String>());
     }
    

    想象一下编译不会失败。
    这意味着setOfArrayList 实例是一个应该包含ArrayList 实例的集合,现在包含LinkedList 元素的列表。

    如果您在 setOfArrayList 上进行迭代,您将不会像预期的那样只拥有 ArrayList&lt;String&gt; 元素。这不安全,这就是编译失败的原因。

    这里是&lt;? super 的示例:

    public void superExample(){
      Set<? super ArrayList<String>> setOfArrayList = new HashSet<>();
    
      // compilation ok
      setOfArrayList.add(new ArrayList<String>());
      // new anonymous type derivating from ArrayList
      ArrayList<String> derivedArrayList = new ArrayList<String>(){
      };
      // compilation ok
      setOfArrayList.add(derivedArrayList);
    }
    

    【讨论】:

    • 太棒了:) ..最后我明白了..谢谢大卫..! .赞赏并接受答案。
    【解决方案2】:

    您不能修改使用&lt;? extends SomeType&gt; 参数化的集合。 Java 只是不允许这样做,因为它不是安全的操作。 add() 修改集合,所以你不能这样做。 &lt;? super SomeType&gt;没有这个限制

    【讨论】:

    • 感谢您回答雅罗斯拉夫...但是请您帮忙找出我的代码中下层方法和上层方法之间的确切区别,其中下层方法编译而上层方法不编译?
    • 呃,不完全是,你不能插入Collection&lt;? extends T&gt;,但你可以插入Collection&lt;? super T&gt;,反之亦然(可以从&lt;? extends T&gt;检索,但不能从&lt;? super T&gt; 检索)。 Iirc 你仍然可以通过强制转换从&lt;? super T&gt; 中检索,但总的来说它是 PECS 的扩展
    • 至于限制的原因...它与类型擦除有关。 Java 不知道运行时的确切参数化,因此修改对于上限集合来说是潜在的危险操作。如果互联网没有这样的限制,有很多可能会出错的例子。
    • 感谢您的耐心朋友.. :) 。我们还能在这里举一个例子吗..? :) 。我有“Effective Java”……但仍然不知何故没有得到这个 100% 清楚的答案……:(
    • 重复的目标已经有示例,请查看。
    【解决方案3】:

    简单地说,我们在编译时不知道upperBoundSet 中包含什么类型的列表。可以是Set&lt;ArrayList&lt;T&gt;&gt;,也可以是Set&lt;LinkedList&lt;T&gt;&gt;,也可以是许多其他选择之一。

    如果结果是Set&lt;LinkedList&lt;T&gt;&gt;,那么添加ArrayList 显然是个坏主意。但是因为我们不知道 set 的内容是什么类型,所以它采取了更安全的选择并阻止它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-22
      • 1970-01-01
      • 2017-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多