【问题标题】:Can someone clarify covariant return types in Java(6)?有人可以澄清 Java(6) 中的协变返回类型吗?
【发布时间】:2013-08-20 02:41:33
【问题描述】:

认为我问的是协变返回类型。我有一些生成的代码正在尝试扩展和使用。假设我有以下两个类:

public class SuperParent
{
    public List<SuperChild> getList()
    {
        return new ArrayList<SuperChild>();
    }
}
public class SuperChild
{
}

现在,我想从这些类中派生出新的类:

public class SubParent extends SuperParent
{
    public List<SubChild> getList()
    {
        return new ArrayList<SubChild>();
    }
}
public class SubChild extends SuperChild
{
}

问题是,显然我不能重写 getList() 方法,因为返回类型不匹配,尽管两个类都在同一个方向上扩展。谁能解释一下?

【问题讨论】:

    标签: java generics overriding covariance


    【解决方案1】:

    您对co-variant 的理解是正确的,但用法不正确。 List&lt;SubChild&gt;List&lt;SuperChild&gt;不一样

    考虑到这一点,List&lt;Animals&gt;List&lt;Dogs&gt; 不同,如果允许的话,事情可能会变得非常糟糕。 DogAnimal 但如果允许分配如下:

    List<Dogs> dogs = new ArrayList<Dogs>();
    List<Animals> animals = dogs; //not allowed.
    

    那么当你添加一只猫时会发生什么?

    animals.add(new Cat());
    

    Dog dog = dogs.get(0); //fail
    

    所以它是不允许的。

    正如许多其他人所建议的那样,使用List&lt;? extends SuperChild&gt; 作为返回类型来解决您的问题。

    编辑 对于您上面的评论,如果您无法控制超级类,恐怕您将无能为力。

    【讨论】:

    • 啊,好的。谢谢,这更清楚了。这太糟糕了;我想知道我是否仍然可以使用 List,并在必要时将变量转换为类型。
    【解决方案2】:

    问题在于泛型 List&lt;SuperChild&gt;List&lt;SubChild&gt; 不兼容,因为如果您在 SubParent 实例上调用 getList() 但通过 SuperParent 接口,您将获得返回值List&lt;SuperChild&gt; 类型。这将允许您添加SuperChild 的其他实例即使列表只允许包含SubChild 的实例(根据SubParent 中定义的返回类型)。

    为了使这个编译改变返回类型为List&lt;? extends SuperChild&gt;,即

    public class SuperParent
    {
      public List<? extends SuperChild> getList()
      {
        return new ArrayList<SuperChild>();
      }
    }
    

    这将允许您返回子类型列表,但不允许您将元素添加到使用超类型返回的列表中(即您不能将元素添加到 List&lt;? extends SuperChild&gt;

    【讨论】:

    • 听起来很合理。现在,我的复杂情况是 SuperParent 是生成的代码 - 我无法更改它。
    • @end-user 如果您不能影响SuperParent 的生成方式,您还必须从SubParent#getList() 返回List&lt;SuperChild&gt;(如果您至少可以影响该代码)。如果您可以在一定程度上影响代码生成,那么在这里不使用泛型也是可行的,尽管这可能会在其他位置产生问题。
    【解决方案3】:

    List&lt;SubChild&gt;不是List&lt;SuperChild&gt;的子类

    java 的泛型没有协变。

    所以,当你尝试协变返回类型时,它实际上是一个不同的类型,java 不允许你完全改变它[因为它不安全]。

    SubParent 中的方法 getList() 应返回 List&lt;SuperChild&gt; [或 ArrayList&lt;SuperChild&gt;, ...] 以解决此问题。

    【讨论】:

      【解决方案4】:

      正如其他人指出的那样,List&lt;SubChild&gt; 不是List&lt;SuperChild&gt; 的子类。

      根据你想要做什么,你可以使用泛型:

      public class SuperParent<T extends SuperChild>
      {
          public List<T> getList()
          {
              return new ArrayList<T>();
          }
      }
      public class SuperChild
      {
      }
      
      public class SubParent extends SuperParent<SubChild>
      {
          public List<SubChild> getList()
          {
              return new ArrayList<SubChild>();
          }
      }
      public class SubChild extends SuperChild
      {
      }
      

      【讨论】:

        【解决方案5】:

        想象一下这样的事情:

        SubParent subParent = new SubParent();
        SuperParent superParent = (SuperParent) subParent; // upcast is okay
        List<SuperChild> list = superParent.getList();
        list.add(new SuperChild());
        

        最后一个语句会违反 SubParent 的合同。

        解决方法是将 SuperParent 的 getList 的合同更改为 List&lt;? extends SuperChild&gt; getList()

        【讨论】:

          猜你喜欢
          • 2014-05-21
          • 1970-01-01
          • 2013-09-12
          • 1970-01-01
          • 2017-01-28
          • 1970-01-01
          • 1970-01-01
          • 2012-07-06
          • 2011-09-02
          相关资源
          最近更新 更多