首页 > 解决方案 > 绑定添加到列表时的一般误解

问题描述

这是一个通配符问题。

我的目的是创建一个列表,其中包含一个具有扩展示例的泛型类型的类:

所以这是结构:

  public class Event<T extends ActionType>{
  }

  public abstract class ActionType{
  }

  //**many** classes that extends ActionType class

这是一个包含从ActionType扩展的类的列表

  private List<Event<ActionType>> list = ArrayList<>();

  Event<RightClick> event = new Event<>(xPosition, yPosition, delay, new RightClick(robot), clicks, robot);

  list.add(event);

我知道我不能这样做以确保我可以添加扩展项目:

  private List<Event<ActionType>> list = ArrayList<>();

但我可以做些什么来将这些项目添加到同一个列表中。

为了解决我的问题,我使用了通配符选择器

 List<Event<? extends ActionType>>

标签: javalistgenericsdata-structures

解决方案


解释

泛型是不变的。AList<Event<ActionType>>不会接受Event<RightClick>,只有Event<ActionType>

了解泛型并调整泛型类型限制。

如果泛型是 covariant,那么您可以给期望 aList<Animal>的人List<Dog>,然后将Cats 添加到它。这会导致堆损坏,因为它dogs.get(0)可能突然变成Cat.

一个例子:

List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());

List<Animal> animals = dogs; // pretend this would work
animals.add(new Cat()); // would be legit

Dog dog = dogs.get(1); // should be safe, but is cat, heap corruption

AList<Animal>明确表示此列表必须接受所有动物,包括猫。但 aList<Dog>仅限于狗。这两个列表的行为不同,它们有不同的限制,因此您不能将一个用于另一个。与 a Dogwho is an不同Animal,a List<Dog>is not an List<Animal>,这就是协变和不变性的含义。


解决方案

“接受任何ActionType,我不在乎”的正确工具是通配符。所以要么去

 List<Event<? extends ActionType>>

要不就

List<Event<?>>

因为Event该类已经指定了T extends ActionType限制。

使用该类型,您将能够向其添加各种Events:

Event<RightClick> rightClick = ...
Event<LeftClick> leftClick = ...
Event<MiddleClick> middleClick = ...

list.add(rightClick);
list.add(leftClick);
list.add(middleClick);

作为?通配符的结果,您将无法在编译时知道实际类型,因此:

Event<?> event = list.get(0); // unknown which exact type

您所知道的?是它至少是extends ActionType,因此您将能够使用 给出的各种方法ActionType,但仅在RightClick示例中没有介绍。这将需要显式转换(由instanceof检查保护),尽管如果您必须在那里使用右键单击特定的东西,我会质疑您的设计。


推荐阅读