【问题标题】:Generics and Inheritance? [duplicate]泛型和继承? [复制]
【发布时间】:2015-05-15 12:50:11
【问题描述】:
public static void main(String... args) {
    List<Child> list1 = new ArrayList<Child>();
    method2(list1);
}

public static void method2(List<Parent> list1) {
}   

我得到以下编译错误

方法method2(List)未定义...

上述问题可以通过将List&lt;Parent&gt; list1修改为List&lt;? extends Parent&gt; list1来解决。

但是如果我尝试像下面这样添加子对象

public static void method2(List<? extends Parent> list1) {
    Child child1 = new Child();
    list1.add(child1);
}

它再次给出编译错误

类型List中的add(capture#1-of ? extends Parent)方法不适用于参数(Child)

所以我的问题是如果List&lt;Child&gt; 可以作为参数传递给List&lt;? extends Parent&gt; list1 为什么我们不能在List&lt;? extends Parent&gt; 下添加子对象?

【问题讨论】:

  • 请张贴子类和父类的定义
  • 查看 PECS 规则。你可以上课Child2 extends Parent。如果允许,您可以将Child 实例添加到List&lt;Child2&gt;
  • 作为一个不相关的注释,我每次看到class Child extends Parent {} 都会开始抽搐。孩子不应该是父母!!! LSVParent parent = new Child(); 是一个无意义的继承类比。
  • 那是因为您从生物学角度考虑 Child 和 Parent,而不是阶级关系。他从未提到 Child 扩展 Parent 并且每个人都理解这个问题的事实说明了这一点。
  • @DanielL。事实上,我经常看到父母/孩子专门被用作生物学类比(参见例如我在 Google 第一页找到的 this tutorial)。此外,如果 OP 使用 class Gidget extends Gadget {},我们就会理解这个问题。可能是任何东西。无论如何,我并不是在批评这个问题。

标签: java generics


【解决方案1】:

这是一个很常见的误解。 Child 扩展 Parent 的事实不会使 List&lt;Child&gt; 扩展 List&lt;Parent&gt;。在这种情况下听起来很不直观,但确实如此。来自java教程:

给定两个具体类型 A 和 B(例如,数字和整数), MyClass 与 MyClass 没有关系,无论是否 不是 A 和 B 是相关的。 MyClass 的共同父级和 MyClass 是对象。

阅读this了解详情。

至于添加到列表中,简短的回答是:假设您有另一个class Child2 extends Parent,现在,您在method2(List&lt;? extends Parent&gt; list1) 处作为参数接收的列表可能是List&lt;Child1&gt;List&lt;Child2&gt; .因此,鉴于第二种情况是可能的,添加 Child1 对象不是类型安全的。

现在,您不能添加的事实并不意味着您不能做其他有用的事情,比如获取大小等。

【讨论】:

  • 第二种情况也显示编译时错误...is not applicable for the arguments (Child2)
  • 当然,它是完全对称的。我举了一个例子,说明为什么两者都会失败,因为您不知道列表中的实际对象类型,而不是说它适用于 Child2
  • 真的很混乱。看来我必须把这个我不喜欢的东西拿出来。
  • 另一种解释方式是:您的方法接收一个参数“扩展父项的内容列表”,但不知道“某事”是什么。因此,您仅限于不需要知道它的操作:列表的属性、选择元素并将其用作普通的父指针等。另一方面,您不能添加新的“东西”不知道应该是什么。
【解决方案2】:

所以我的问题是List&lt;Child&gt; 是否可以作为参数传递给List&lt;? extends Parent&gt; list1 为什么我们不能在List&lt;? extends Parent&gt; 下添加子对象?

假设我们可以。现在假设我们有:

class Parent {}
class Mother extends Parent {}
class Father extends Parent {}

static void m(List<? extends Parent> parents) {
    parents.add(new Father());
}

List<Mother> mothers = new ArrayList<>();
m(mothers);
// throws 'ClassCastException: cannot cast Father to Mother'
Mother actuallyAFather = mothers.get(0);

List&lt;? extends Parent&gt; 是一个 List,它最多存储 Parent,或者可能是 Parent 的某个子类型,我们不再了解它。除了 null 之外,我们不能向它添加任何内容。

所以也许你想要的是:

public static void method2(List<? super Child> list1) {
//                                ^^^^^^^^^^^
    Child child1 = new Child();
    list1.add(child1);
}

List&lt;? super Child&gt; 是一个列表,我们可以将Child 添加到其中。也许是List&lt;Parent&gt; 或者是List&lt;Child&gt;,但我们不在乎。我们只关心它是一个可以添加Child 的列表。

另见:

【讨论】:

    【解决方案3】:

    来自Wildcards

    List&lt;? extends Parent&gt; 是有界通配符的一个示例。这 ?代表未知类型,就像我们之前看到的通配符一样。但是,在这种情况下,我们知道这种未知类型实际上是Parent 的子类型。

    因此,请确保您的 Child 类扩展了 Parent 类。那么下面的代码就可以工作了。

    public static void method2(List<? extends Parent> list1) {
    

    通过此更改,它应该可以工作,请参阅Output of you program

    【讨论】:

    • 他自己这么说的。他的问题是当您尝试添加到该列表时会发生什么。
    【解决方案4】:

    这是我阅读更多内容后的答案

    我们不能指定List&lt;Dog&gt; to List&lt;Animal&gt;,因为List&lt;Animal&gt;意味着我们可以添加任何动物,比如猫、狗 而List&lt;Dog&gt; 意味着我们只能添加狗/子类型,但不能添加猫。所以分配者会认为他只会得到狗/子类型 但他也可以得到猫对象(所以运行时错误)

    这是一个例子

    psvm{
    List<Dog> list1 = new ArrayList<Dog>();
    list1.add(new Dog()); 
    method1(list1);// compilation error
    }
    
    public static void method1(List<Animal> list1) {
    list1.add(cat); // Good
    list1.add(dog); // Good
    }
    

    另一个常见的误解是关于上界泛型,它与

    完全相反

    当我们说List&lt;? extends Animal&gt; list1 时,我们不能添加除 null 之外的任何值,但是可以分配任何 Animal/Sub 类型 如果我们允许添加它意味着它可以包含任何动物,如猫、狗,但是在检索(即向下投射)时,我们实际上并没有 它是类型。

    psvm{
    List<Dog> list1 = new ArrayList<Dog>();
    list1.add(new Dog()); 
    method1(list1);// good
    }
    
    public static void method1(List<? extends Animal> list1) {
    list1.add(cat); // compilation error
    list1.add(dog); // compilation error
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-30
      • 2023-03-25
      • 1970-01-01
      • 2011-03-06
      • 1970-01-01
      相关资源
      最近更新 更多