mzoz
mzoz

Reputation: 1353

Java add subtype instance to a collection of supertype?

The following code compiles and runs without problem:

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);
    }
}

I'm baffled with this line of code

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

res is of type HashSet<List<Integer>>, and Arrays.asList returns value of type ArrayList (not java.util.ArrayList, but an internally defined class according to java 8 source code, which also implements java.util.List)

Why DOESN'T adding ArrayList instance to a collection of List raise exception?

Although the former is subtype of latter, as far as I know the adding operation should not be allowed because HashSet<ArrayList> is not subtype of Set<List>?

Upvotes: 3

Views: 881

Answers (1)

ernest_k
ernest_k

Reputation: 45339

Quoting the generics FAQ on the compatibility you're questioning:

A List<Object> is compatible to a Collection<Object> because the two types are instantiations of a generic supertype and its generic subtype and the instantiations are for the same type argument Object.

The types that are incompatible are those (related ones) that have different type arguments (event if those argument types are otherwise related/compatible) - same source:

A ArrayList<String> object cannot be passed as argument to a method that asks for a ArrayList<Object> because the two types are instantiations of the same generic type, but for different type arguments, and for this reason they are not compatible with each other.

This:

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

Is doing an instantiation of the first kind above.


Although the former is subtype of latter, as far as I know the adding operation should not be allowed because HashSet<ArrayList<Integer>> is not subtype of Set<List<Integer>>

You have one wrong fact here. The static return type of Arrays.asList(nums[i],nums[j++],nums[k--]) is not ArrayList<Integer>. It's List<Integer>. You have good insight into the runtime class returned by asList, but the compiler does not care about that (except when validating that code itself), it stops at the static type (the declared return type) of asList, which in this case is List<Integer>.

It's true that HashSet<ArrayList<Integer>> is not compatible with Set<List<Integer>>, but this is irrelevant because the compiler is not looking at the actual runtime class (even if this is what you use to instantiate the set).
As far as I can see, all that the compiler needs to enforce here is that you're calling Set<List<Integer>>.add() with an argument that is a List<Integer>, which you are doing.

Upvotes: 2

Related Questions