为什么Java无法将ArrayList<TreeSet<Integer>>转换为List<Set<Object>>?

5

我想做的事情似乎很简单:计算一组集合的大小总和。Netbeans给出以下警告/错误:

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

对于以下两段代码:

/**
 * 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;
}

然后使用以下代码调用它:

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

最后一行出现编译错误:

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

那么,为什么不能将 java.lang.Integer 转换为 java.lang.Object?

1
正确的问题标题应该是:“为什么Java无法将ArrayList<TreeSet<Integer>>转换为List<Set<Object>>?” - Kirk Woll
4个回答

14

List<Integer>不是List<Object>。如果Java允许这样做,那么你就可以使用List<String>调用该方法,并且程序将会出错。正如Jeremy Heiler所指出的,你可以使用List<? extends Object>来解决问题。这意味着可以接受所有继承自Object的类型。在泛型术语中,?被称为wildcard


1
然而,一个 List<Integer> 是一个 List<? extends Object> - Jeremy
使用 Set<? extends Object> 会导致 actual argument java.util.ArrayList<java.util.TreeSet<java.lang.Integer>> cannot be converted to java.util.List<java.util.Set<? extends java.lang.Object>> - MrDrews
使用 List<? extends Set<? extends Object>> :) - Petar Minchev
@Petar:您在评论中提供的解决方案有效,但是为什么Java不允许这样做的解释是不正确的。用List<Set<String>>调用sizeOfListOfSets(List < Set<Object>>)不会导致任何概念上的问题。问题只会出现在方法修改参数对象时。 - Chris Lercher
@Petar:是的,那是肯定的。但问题是/曾经是:为什么Java不能简单地允许这样做... - Chris Lercher
显示剩余3条评论

2

任何你声明为通用类型的东西,必须始终是完全相同的通用类型。唯一可以变化的是基本类型即:

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

这是因为,例如,如果您声明了一个参数为List<Object>,并且您可以传递一个List<Integer>,那么您将能够向该列表中添加任何类型的对象,这将破坏类型安全性。
虽然有一种使用通配符?的解决方法,但除非您像这样做<? super MyObject>,否则仍然无法添加元素,因为继承树上方的任何内容都可以添加到列表中。

2
问题不在于将整数转换为对象,而是将整数列表转换为对象列表失败,因为List<Integer>不是List<Object>。Java编译器不会尝试自动转换泛型类型。
您可以更改方法声明,以类似以下的方式避免错误:
public static int sizeOfListOfSets(List<Set<? extends Object>> list)

请查看此链接以获取更多信息:http://download.oracle.com/javase/tutorial/java/generics/subtyping.html - n0rm1e
那仍然导致了一个错误,但是 public static int sizeOfListOfSets(List<? extends Set<?>> list) 没有。感谢回复。 - MrDrews
@houman001 - 必须是 List<? extends Set<? extends Object>> :) - Petar Minchev

0

这样可以让你将任何对象放入该列表中。当您稍后从原始列表引用访问元素时,它将包含非整数对象,尽管该列表仍被声明为List<Integer> - 这意味着您不能再依赖于类型所说的内容了。例如:

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.
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接