为什么List <String>不能作为List <Object>?

use*_*518 30 java generics casting

考虑下面doSomething(List<Object>)接受List<Object>作为参数的方法.

private void doSomething(List<Object> list) {
    // do something
}
Run Code Online (Sandbox Code Playgroud)

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

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

doSomething(stringList); // compilation error incompatible types
doSomething(objectList); // works fine 
Run Code Online (Sandbox Code Playgroud)

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

objectList = stringList;  // compilation error incompatible types
Run Code Online (Sandbox Code Playgroud)

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

Ahm*_*leh 43

因为虽然String延伸Object,List<String>但不延伸List<Object>

更新:
通常,如果Foo是子类型(子类或子接口)Bar,并且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
Run Code Online (Sandbox Code Playgroud)

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

有关 Java Tutorial页面的更多详细说明.

  • 虽然这个答案是真的,但看起来更像是对OP问题的确认.恕我直言,它缺少最重要的部分:"为什么`List <String>`不是`List <Object>`的子类型?". (4认同)
  • +1; 无论如何你可以调用doSomething(((List <Object>)(List <?>)stringList)) (2认同)

Mar*_*o13 17

您可以将错误类型的对象放入列表中,如果这有效:

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
Run Code Online (Sandbox Code Playgroud)

  • PS:关于泛型的超类型/子类型关系在http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ207中得到了很好的解释. (2认同)

Shi*_*mar 10

对于任何不熟悉泛型的人来说,Java中的这个通用问题可能看起来很混乱,因为乍一看它看起来像是对象,所以List<String>可以在List<Object>需要的地方使用,但事实并非如此.这将导致编译错误.

这有一定道理,如果你走一步,因为List<Object> 可以存储任何东西,包括String,Integer等,但List<String>只能存储Strings.

另请看:为什么不从List <T>继承?

  • 问题并不完全是被调用者可以存储任何内容:真正的问题是,如果允许,调用者将无法确定是否具有String实例,因此必须像在pre-generics天中那样处理它(instanceof运算符) ). (5认同)
  • 选择作为答案,因为它让我更好地理解. (3认同)

Nic*_*olt 5

这些限制的原因与方差考虑有关。

采取以下代码:

public void doSomething(List<Object> objects)
{
  objects.add(new Object());
}
Run Code Online (Sandbox Code Playgroud)

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

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

doSomething(strings);

for (String s : strings)
{
  System.out.println(s.length);
}
Run Code Online (Sandbox Code Playgroud)

希望很明显,如果编译器允许编译此代码(但事实并非如此),为什么这会中断 -当尝试将 转换为 aClassCastException时,列表中的第二项会出现aObjectString

为了能够传递通用集合类型,您需要执行以下操作:

public void doSomething(List<?> objects)
{
  for (Object obj : objects)
  {
    System.out.println(obj.toString);
  }
}
Run Code Online (Sandbox Code Playgroud)

同样,编译器正在注视着您,如果您将 替换System.outobjects.add(new Object())编译器,编译器将不允许这样做,因为objects可能已创建为List<String>.

有关方差的更多背景信息,请参阅维基百科文章协方差和逆变