为什么List 不能作为List ?

考虑下面的方法doSomething(List)接受List作为参数。

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

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

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

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

 objectList = stringList; // compilation error incompatible types 

我的问题是为什么List无法传递给接受List

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

如果你更进一步,这是有道理的,因为List 可以存储包括StringInteger等在内的任何内容,但List只能存储Strings

另请看: 为什么不从List inheritance?

因为String扩展了ObjectList不扩展List

更新:
通常,如果FooBar的子类型(子类或子接口),并且G是一些generics类型声明,则G不是G的子类型。

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

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

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

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

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

 private void doSomething(List list) { list.add(new Integer(123)); // Should be fine, it's an object } List stringList = new ArrayList(); doSomething(stringList); // If this worked.... String s = stringList.get(0); // ... you'd receive a ClassCastException here 

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

请使用以下代码:

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

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

 List strings = new ArrayList(); string.add("S1"); doSomething(strings); for (String s : strings) { System.out.println(s.length); } 

希望显而易见的是,如果编译器允许编译此代码(它没有),这会破坏为什么 – 当尝试将Object转换为String时,列表中的第二个项会发生ClassCastException

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

 public void doSomething(List objects) { for (Object obj : objects) { System.out.println(obj.toString); } } 

同样,编译器正在观察你的后面,你是否要使用objects.add(new Object())替换System.out ,编译器不允许这样做,因为objects可能已经创建为List

有关Variance的更多背景,请参阅维基百科的艺术协方差和逆变

有时期望ListList的超类型,因为ObjectString的超类型。

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

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

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

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

点击此处了解更多详情

来自generics的Java教程:

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

 List ls = new ArrayList(); // 1 List lo = ls; // 2 

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

那么,看看接下来的几行:

 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不再仅仅持有字符串,当我们尝试从中获取某些东西时,我们会得到一个粗鲁的惊喜。

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

来源: generics和子类型

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

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

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