Why can't Java convert an ArrayList>

2020-07-16 09:16发布

问题:

I'm attempting to do something that seems rather simple: sum the sizes of a list of sets. Netbeans gives the following warning/error:

actual argument java.util.ArrayList<java.util.TreeSet<java.lang.Integer>> cannot be    converted to java.util.List<java.util.Set<java.lang.Object>> by method invocation conversion

for the following two pieces of code:

/**
 * Sums the sizes of all sets in a list.  Note that while there will be
 * no duplicate elements in a single set, "sister" sets may contain
 * elements, so the value returned is **not** equal to the number of unique
 * elements in all sets.
 * @param list, a List of Sets
 * @return the number of elements contained in all sets
 */
public static int sizeOfListOfSets(List<Set<Object>> list) {
    int size = 0;

    for (Set<Object> set : list) {
        size += set.size();
    }

    return size;
}

and then calling it with the following:

    ArrayList<TreeSet<Integer>> testList = new ArrayList<TreeSet<Integer>>();
    TreeSet<Integer>            testSet;
    int                         size = 0;

    testSet = new TreeSet<Integer>();
    testSet.add(new Integer(++size));
    testSet.add(new Integer(++size));
    testList.add(testSet);
    testSet = new TreeSet<Integer>();
    testSet.add(new Integer(++size));
    testList.add(testSet);

    int expResult = size;
    int result    = Helpers.sizeOfListOfSets(testList);

the last line gives the compilation error:

error: method sizeOfListOfSets in class Helpers cannot be applied to given types;
1 error

So, why can't java.lang.Integer be converted to java.lang.Object?

回答1:

A List<Integer> is not a List<Object>. If Java allowed that, then you could call the method with List<String> and you will be broken. As Jeremy Heiler pointed out you can use List<? extends Object> and you will be fine. This means every type which extends Object is allowed. ? is called a wildcard in generic jargon.



回答2:

Anything you declare as a generic type must be the exact same generic type always. The only think that can vary is the base type i.e.:

List<MyObject> myList = new ArrayList<MyObject>;

This is because for example if you had a parameter declared as List<Object>, and you could pass it a List<Integer>, then you would be able to add ANY kind of object to that list, which would break the type safety.

Although there is a workaround using the wildcard ?, you still will NOT be able to add elements unless you do it like this <? super MyObject>, because anything higher on the inheritance tree would be ok to add to the list.



回答3:

The issue is not converting from an Integer to an Object, but from a list of Integer to a list of Object which fails because List<Integer> is not a List<Object>. Java compiler does not try to automatically cast generic types.

You might change your method declaration to something like this to get away with the error:

public static int sizeOfListOfSets(List<Set<? extends Object>> list)


回答4:

Because that would enable you to put any object in that list. When you access elements later from your original list reference, it will contain non-Integer objects, although the list is still declared as a List<Integer> - which means that you can't rely on what the type says anymore. Example:

List<Integer> intList = new ArrayList<Integer>();
doSomething(intList);
for (Integer i : intList) {
    // i must be an Integer, so doSomething must not
    // be able to put non-Integers into that list.
}


标签: java generics