为什么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
可以存储包括String
, Integer
等在内的任何内容,但List
只能存储Strings
。
另请看: 为什么不从List
因为String
扩展了Object
, List
不扩展List
更新:
通常,如果Foo
是Bar
的子类型(子类或子接口),并且G
是一些generics类型声明,则G
不是G
的子类型。
这是因为collections确实发生了变化。 在您的情况下,如果List
是List
的子类型,则在使用其超类型引用列表时,可以向其添加除String
的类型,如下所示:
List stringList = new ArrayList ; List
并且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的更多背景,请参阅维基百科的艺术协方差和逆变
有时期望List
是List
的超类型,因为Object
是String
的超类型。
这种期望源于这样一种事实:数组存在这种类型的关系:
Object[]
是String[]
的超类型,因为Object
是String
的超类型。 (这种类型的关系称为协方差。)
组件类型的超子类型关系扩展到相应的数组类型。
对于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