有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java为什么列表<String>不能作为列表<Object>接受?

考虑下面的方法^ {< CD1>},它接受^ {< CD2>}作为参数。

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

现在考虑下面的代码片段,它试图调用^ {< CD3>},在这里我试图通过^ {< CD4> }到^ {CD3>}/P>

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>的方法


共 (6) 个答案

  1. # 1 楼答案

    来自泛型Java教程:

    让我们测试一下您对泛型的理解。以下代码段合法吗

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

    第一行当然是合法的。问题中比较棘手的部分是第2行。这归结为一个问题:字符串列表是对象列表吗。大多数人本能地回答:“当然!”

    好的,看下面几行:

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

    在这里,我们将ls和lo化名为。通过别名lo访问ls(字符串列表),我们可以在其中插入任意对象。因此,ls不再仅仅是一个字符串,当我们试图从中得到一些东西时,我们会得到一个粗鲁的惊喜

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

    资料来源:Generics and Subtyping

  2. # 2 楼答案

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

    以下面的代码为例:

    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);
    }
    

    希望很明显,如果编译器允许编译这段代码(它不允许),那么这段代码将中断—当试图将Object转换为String时,列表中的第二项将出现ClassCastException

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

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

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

    有关差异的更多背景信息,请参阅维基百科文章Covariance and contravariance

  3. # 3 楼答案

    如果有效,则可能会将错误类型的对象放入列表

    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
    
  4. # 4 楼答案

    因为String扩展Object,而List<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编译器必须防止这些情况

    更多关于thisJava教程页面的详细说明

  5. # 5 楼答案

    有时可以预期List<Object>将是List<String>的超类型,因为ObjectString的超类型

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

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

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

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

    有关详细信息,请查看here

  6. # 6 楼答案

    Java中的这个泛型问题可能会让任何不太熟悉泛型的人感到困惑,因为乍一看,字符串就是对象,所以List<String>可以在需要List<Object>的地方使用,但事实并非如此。这将导致编译错误

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

    还可以看看:Why not inherit from List<T>?