请考虑以下方法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>
的方法?
答案 0 :(得分:41)
因为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
并且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>
可以存储任何,包括String
,Integer
等,但List<String>
只能商店Strings
。
答案 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>
的超类型,因为Object
是String
的超类型。
这种期望源于数组存在这种类型关系的事实:
Object[]
是String[]
的超类型,因为Object
是String
的超类型。 (这种类型的关系称为协方差。)
组件类型的超子类型关系扩展到相应的数组类型。
对于泛型类型的实例化,不存在这种类型关系。 (参数化类型不是协变的。)
查看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行将导致编译时错误。
答案 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