Java将子类型实例添加到超类型的集合?

时间:2019-10-08 07:18:09

标签: java generics

以下代码可以编译并正常运行:

class Solution {
    public  List<List<Integer>> threeSum(int[] nums) {
        Set<List<Integer>> res  = new HashSet<>();
        if(nums.length==0) return new ArrayList<>(res);
        Arrays.sort(nums);
        for(int i=0; i<nums.length-2;i++){
            int j =i+1;
           int  k = nums.length-1;
            while(j<k){
                int sum = nums[i]+nums[j]+nums[k];
                if(sum==0)res.add(Arrays.asList(nums[i],nums[j++],nums[k--]));
                else if ( sum >0) k--;
                else if (sum<0) j++;
            }

        }
        return new ArrayList<>(res);
    }
}

我对这行代码感到困惑

res.add(Arrays.asList(nums[i],nums[j++],nums[k--]))

res的类型为HashSet<List<Integer>>Arrays.asList返回的类型为ArrayList的值(不是java.util.ArrayList,而是根据Java 8内部定义的类{ {3}},它也实现了java.util.List

为什么 ArrayList实例添加到List的集合中会引发异常?

尽管前者是后者的子类型,但据我所知,由于HashSet<ArrayList>不是Set<List>的子类型,因此不允许加法运算吗?

1 个答案:

答案 0 :(得分:2)

generics FAQ引用要问的兼容性:

  

List<Object>Collection<Object>兼容,因为这两种类型是泛型超类型的实例化及其泛型子类型,并且这些实例化是针对同一类型参数Object的。

不兼容的类型是具有不同类型参数的(相关的)类型(如果这些参数类型相关或兼容,则为事件)-相同的来源:

  

ArrayList<String>对象不能作为参数传递给要求ArrayList<Object>的方法,因为这两种类型是相同泛型的实例化,但是对于不同类型的参数,因此,它们彼此不兼容。

此:

  • Arrays.asList(nums[i],nums[j++],nums[k--]) //creating and adding List<Integer>

正在执行上面的第一种实例化。


  

尽管前者是后者的子类型,但据我所知,由于HashSet<ArrayList<Integer>>不是Set<List<Integer>>的子类型,因此不允许进行加法运算

您在这里有一个错误的事实。 Arrays.asList(nums[i],nums[j++],nums[k--])的静态返回类型为 not ArrayList<Integer>。是List<Integer>。您对asList返回的运行时类有很好的了解,但是编译器对此并不关心(除非验证该代码本身),它停留在{{1}的静态类型(声明的返回类型)上},在这种情况下为asList

确实List<Integer>HashSet<ArrayList<Integer>>不兼容,但这无关紧要,因为编译器没有查看实际的运行时类(即使这是用于实例化集合的方法)。
据我所知,编译器需要在此处强制执行的所有操作是,您正在使用Set<List<Integer>>作为参数来调用Set<List<Integer>>.add()