5

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?

MrDrews
  • 2,139
  • 2
  • 22
  • 22

4 Answers4

14

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.

Petar Minchev
  • 46,889
  • 11
  • 103
  • 119
  • 1
    However, A `List` is a `List extends Object>`. – Jeremy Aug 08 '11 at 16:26
  • Using `Set extends Object>` leads to `actual argument java.util.ArrayList> cannot be converted to java.util.List>` – MrDrews Aug 08 '11 at 16:55
  • Use `List extends Set extends Object>>` :) – Petar Minchev Aug 08 '11 at 17:01
  • @Petar: The solution you provided in your comments works, but the explanation (_why_ Java does not allow this) is not correct. Calling `sizeOfListOfSets(List>)` with a `List>` wouldn't lead to any conceptual problem. The problem only appears when a method modifies the parameter object. – Chris Lercher Aug 08 '11 at 17:34
  • @Chris: Calling `sizeOfListOfSets(List>)` with a `List>` will cause a compile-time error. – Petar Minchev Aug 08 '11 at 17:41
  • @Petar: Yes, that's for sure. But the question is/was: Why can't Java simply allow this... – Chris Lercher Aug 08 '11 at 17:49
  • @Chrhis: "won't lead to any conceptual problem" Of course it can. There is no guarantee from just looking at that signature that the function sizeOfListOfSets won't try to add an Object into one of the Sets in the List, which *would* be a problem. *You* may happen to know that the function doesn't do that, but if that is the case, then it should use `? extends` – newacct Aug 09 '11 at 04:34
  • @newacct: This is exactly correct, and that's what Petar's answer _should_ have pointed out (but it did not, and the current answer is rather misleading IMO). – Chris Lercher Aug 09 '11 at 09:09
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.

Oscar Gomez
  • 18,436
  • 13
  • 85
  • 118
2

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)
n0rm1e
  • 3,796
  • 2
  • 28
  • 37
0

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.
}
Chris Lercher
  • 37,264
  • 20
  • 99
  • 131