List<Dog> 是 List<Animal> 的子类吗?为什么 Java 泛型不是隐式多态的?

新手上路,请多包涵

我对 Java 泛型如何处理继承/多态性感到有点困惑。

假设以下层次结构 -

动物(父母)

- (儿童)

所以假设我有一个方法 doSomething(List<Animal> animals) 。 By all the rules of inheritance and polymorphism, I would assume that a List<Dog> is a List<Animal> and a List<Cat> is a List<Animal> - and所以任何一个都可以传递给这个方法。不是这样。如果我想实现这种行为,我必须通过说 doSomething(List<? extends Animal> animals) 明确告诉该方法接受 Animal 的任何子类的列表。

我明白这是 Java 的行为。我的问题是 _为什么_?为什么多态一般都是隐式的,但是涉及到泛型就一定要指定?

原文由 froadie 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 589
2 个回答

不, List<Dog> 不是 List<Animal> 。考虑一下你可以用 List<Animal> 做什么——你可以向它添加 任何 动物……包括猫。现在,你能合乎逻辑地将一只猫添加到一窝小狗中吗?绝对不。

 // Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?

突然你有一只 非常 困惑的猫。

现在,您 不能Cat 添加到 List<? extends Animal> 因为您不知道它是 List<Cat> 。您可以检索一个值并知道它将是 Animal ,但您不能添加任意动物。对于 List<? super Animal> 则相反 - 在这种情况下,您可以安全地向其添加 Animal ,但您不知道可以从中检索到什么,因为它可能是一个 List<Object>

原文由 Jon Skeet 发布,翻译遵循 CC BY-SA 3.0 许可协议

您正在寻找的是 _covariant type parameters_ 。这意味着如果一种类型的对象可以在方法中替换为另一种类型(例如, Animal 可以替换为 Dog ),这同样适用于使用这些对象的表达式(所以 List<Animal> 可以替换为 List<Dog> )。问题是协变对于一般的可变列表来说是不安全的。假设你有一个 List<Dog> ,它被用作 List<Animal> 。当您尝试向这个 List<Animal> 添加一个 Cat 时会发生什么,它实际上是一个 List<Dog> ?自动允许类型参数协变会破坏类型系统。

添加允许将类型参数指定为协变的语法会很有用,这避免了方法声明中的 ? extends Foo ,但这确实增加了额外的复杂性。

原文由 Michael Ekstrand 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题