为什么List <string>不能作为List <object>?</object> </string>

时间:2014-03-03 10:47:32

标签: java generics casting

请考虑以下方法doSomething(List<Object>)接受List<Object>作为参数。

private void doSomething(List<Object> list) {
    // do something
}

现在考虑下面的代码片段,它尝试调用doSomething()我试图将List<String>传递给doSomething()

List<Object> objectList;
List<String> stringList;

doSomething(stringList); // compilation error incompatible types
doSomething(objectList); // works fine 

即使是下面的代码也会引发编译错误

objectList = stringList;  // compilation error incompatible types

我的问题是为什么List<String>无法传递给接受List<Object>的方法?

7 个答案:

答案 0 :(得分:41)

因为String延伸ObjectList<String>不延伸List<Object>

<强>更新
通常,如果FooBar的子类型(子类或子接口),并且G是一些泛型类型声明,则G<Foo>不是子类型的情况G<Bar>

这是因为收藏品确实发生了变化。在您的情况下,如果List<String>List<Object>的子类型,则在使用其超类型引用列表时,可以向其添加除String以外的类型,如下所示:

List<String> stringList = new ArrayList<String>;
List<Object> objectList = stringList;// this does compile only if List<String> where subtypes of List<Object>
objectList.add(new Object());
String s = stringList.get(0);// attempt to assign an Object to a String :O

并且Java编译器必须防止这些情况。

this Java Tutorial页面上详细阐述。

答案 1 :(得分:16)

您可以将错误类型的对象放入 IF 列表中:

private void doSomething(List<Object> list) {
    list.add(new Integer(123)); // Should be fine, it's an object
}

List<String> stringList = new ArrayList<String>();
doSomething(stringList); // If this worked....
String s = stringList.get(0); // ... you'd receive a ClassCastException here

答案 2 :(得分:10)

Java中的这个通用问题可能会让任何对Generics不太熟悉的人感到困惑,因为乍一看它看起来像String是对象所以List<String>可以在需要List<Object>的地方使用但是这个不是真的。这将导致编译错误。

如果你更进一步,这是有道理的,因为List<Object> 可以存储任何,包括StringInteger等,但List<String>只能商店Strings

另请参阅:Why not inherit from List<T>?

答案 3 :(得分:4)

这些限制的原因与差异因素有关。

请使用以下代码:

public void doSomething(List<Object> objects)
{
  objects.add(new Object());
}

扩展您的示例,您可以尝试执行以下操作:

List<String> strings = new ArrayList<String>();
string.add("S1");

doSomething(strings);

for (String s : strings)
{
  System.out.println(s.length);
}

希望显而易见的是,如果编译器允许编译此代码(这不会),这将会破坏 - 在尝试强制转换{{1时,列表中的第二项会发生ClassCastException转到Object

为了能够传递通用集合类型,您需要这样做:

String

同样,编译器正在观察您的背景,您是否要将public void doSomething(List<?> objects) { for (Object obj : objects) { System.out.println(obj.toString); } } 替换为编译器不允许的System.out,因为objects.add(new Object())可能已创建为objects }。

有关Variance的更多背景,请参阅Wikipedia artical Covariance and contravariance

答案 4 :(得分:3)

有时候期望List<Object>List<String>的超类型,因为ObjectString的超类型。

这种期望源于数组存在这种类型关系的事实:

Object[]String[]的超类型,因为ObjectString的超类型。 (这种类型的关系称为协方差。)

组件类型的超子类型关系扩展到相应的数组类型。

对于泛型类型的实例化,不存在这种类型关系。 (参数化类型不是协变的。)

查看here了解详情

答案 5 :(得分:2)

来自泛型的Java教程:

让我们测试一下你对泛型的理解。以下代码段是否合法?

List<String> ls = new ArrayList<String>(); // 1
List<Object> lo = ls; // 2 

第1行肯定是合法的。问题的棘手部分是第2行。这归结为一个问题:是一个String列表对象列表。大多数人本能地回答:“当然!”

好吧,看看接下来几行:

lo.add(new Object()); // 3
String s = ls.get(0); // 4: Attempts to assign an Object to a String!

这里我们别名ls和lo。通过别名lo访问ls,一个String列表,我们可以在其中插入任意对象。因此,ls不再仅仅支持Strings,当我们尝试从中获取某些东西时,我们会得到一个粗鲁的惊喜。

Java编译器当然会阻止这种情况发生。第2行将导致编译时错误。

来源:Generics and Subtyping

答案 6 :(得分:0)

如果您不确定它将采用什么数据类型,您可以使用Java中的Generics,如下所示

public static void doSomething(List<?> data) {

}

public static void main(String [] args) {
    List<Object> objectList = new ArrayList<Object>();
    List<String> stringList = new ArrayList<String>();
    doSomething(objectList);
    doSomething(stringList);
}

但在使用数据时,您需要将正确的数据类型指定为Type Cast