【问题标题】:Why List<String> is not acceptable as List<Object>? [duplicate]为什么 List<String> 不能作为 List<Object> 接受? [复制]
【发布时间】:2014-04-04 09:03:50
【问题描述】:

考虑下面的方法doSomething(List&lt;Object&gt;),它接受List&lt;Object&gt;作为参数。

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

现在考虑下面的代码 sn-p 尝试调用 doSomething() 我尝试将 List&lt;String&gt; 传递给 doSomething()

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

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

即使下面的代码也会抛出编译错误

objectList = stringList;  // compilation error incompatible types

我的问题是为什么不能将List&lt;String&gt; 传递给接受List&lt;Object&gt; 的方法?

【问题讨论】:

标签: java generics casting


【解决方案1】:

因为String 扩展了Object,List&lt;String&gt; 没有扩展List&lt;Object&gt;

更新:
一般来说,如果Foo 是Bar 的子类型(子类或子接口),而G 是一些泛型类型声明,则G&lt;Foo&gt; 不是G&lt;Bar&gt; 的子类型。

这是因为集合确实发生了变化。在您的情况下,如果 List&lt;String&gt; 是 List&lt;Object&gt; 的子类型,则当使用其超类型引用列表时,可以将 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 教程页面上的详细说明。

【讨论】:

  • +1;无论如何你可以调用 doSomething(((List)(List>) stringList))
【解决方案2】:

你可以把一个错误类型的对象放入列表中如果这行得通:

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

【讨论】:

【解决方案3】:

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

如果你更进一步,这确实有意义,因为List&lt;Object&gt; 可以存储任何东西,包括String、Integer 等,但List&lt;String&gt; 只能存储Strings。

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

【讨论】:

  • 问题并不完全是被调用者可以存储任何东西:真正的问题是,如果允许的话,调用者将不确定是否拥有 String 实例,因此必须将其处理为在泛型之前的日子里(instanceof 运算符)。
【解决方案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);
}

希望很明显,如果编译器允许编译此代码(它不允许编译),为什么这会中断 - 当尝试将 Object 转换为String.

为了能够传递通用集合类型,您需要这样做:

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

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

有关 Variance 的更多背景信息,请参阅 Wikipedia artical Covariance and contravariance

【讨论】:

    【解决方案5】:

    有时会期望 List&lt;Object&gt; 是 List&lt;String&gt; 的超类型,因为 Object 是 String 的超类型。

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

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

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

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

    查看here了解更多详情

    【讨论】:

      【解决方案6】:

      来自 Java 泛型教程:

      让我们测试一下您对泛型的理解。下面的代码sn-p合法吗?

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

      第 1 行当然是合法的。问题的棘手部分是第 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

      【讨论】:

        【解决方案7】:

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

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

        但是在使用数据时,您需要指定正确的数据类型作为类型转换

        【讨论】:

        • 我认为与问题无关
        • 是的,我知道与问题无关,但是由于您在问题中添加了适当的评论,因此认为在 Java 中使用泛型分享可能的泛型方式会很好:-/
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-09
        • 2012-10-03
        • 2012-06-27
        • 1970-01-01
        • 2012-11-10
        相关资源
        最近更新 更多