关于Java:带有疑问标记的泛型列表类型

Generic list type with question mark

本问题已经有最佳答案,请猛点这里访问。

我很难解释/理解Java列表的泛型类型:

1
List<? extends Command> myVar = client.performAction(actionParams);

通用类型? extends Command是如何调用的,比如它有名称吗?这是什么类型的?它是一个Command对象吗?或者这是否意味着它只接受扩展Command的类?使用这种结构我的优势是什么?在什么样的Java版本中集成了这种类型的结构?


上界通配符用于放宽可以使用的对象的类型限制。在这种情况下,您接受扩展/实现Command类型的所有内容。


关于List的重要一点是它是一个抽象类型。你不能写new List()并期望一些东西被创造出来。有两个原因。

  • List是一个接口,具体实现如ArrayListLinkedList等。
  • 类型参数中的通配符,表示"这可以是Command的子类型,包括Command本身"的任何类型。

这意味着List类型的变量可以引用这些具体类型的对象

  • ArrayList
  • LinkedList
  • CopyOnWriteArrayList

以及许多其他组合。当然,假设SpecialCommandImportantCommandCommand的亚型。

当您创建一个该变量将要引用的对象时,您需要明确它是什么具体类型。例如

1
List<? extends Command> myCommandList = new ArrayList<SpecialCommand>();

当然,一旦完成了这些操作,您就可以在myCommandList上调用一些常见的List方法,例如

1
Command firstCommand = myCommandList.get(0);

很好,因为我们知道无论列表中的对象是什么,它们都是某种类型的Command。但你不能做这样的事

1
2
SpecialCommand mySpecialCommand = new SpecialCommand();
myCommandList.add(mySpecialCommand);

因为编译器无法知道您正在向列表中添加正确类型的对象。此时,myCommandList可以是LinkedList或类似的文件,在其中添加SpecialCommand需要编译器阻止。

这意味着,如果您有一个变量

  • 你不在乎它是什么样的清单(ArrayListLinkedList或其他什么)
  • 你不在乎名单上有什么样的Command
  • 您不会试图向列表中添加任何内容。

这意味着您通常不会将它用于局部变量或字段。它更可能是一个方法参数,其中传入的内容可能是LinkedList或其他任何东西;但在该方法中,您要做的只是将对象从列表中取出,并对它们执行Command类型的操作。

泛型自Java 5以来一直在Java中,包括通配符。


List被称为上界通配符(许多答案已经解决了这个问题)。一般来说,没有人会像这样声明他或她的列表;这主要用于方法(更常见的是,在静态方法中)。

List是指含有一种特定类型Command的列表(即一个特定的子类)。因为它是一个未知的子类类型,所以在编译时,您将无法添加任何内容。您的IDE可能会将add方法解析为add(null e)方法。

List虽然看起来很相似,但它指的是一个拥有任何东西的列表,只要它是Command(或Command的子类)。

例子:

1
2
3
4
5
6
List<? extends Command> commands = new ArrayList<FooCommand>();
commands.add(new FooCommand()); // Not allowed at compile time

List<Command> commands = new ArrayList<>();
commands.add(new FooCommand());
commands.add(new BarCommand());

使用时的示例:

1
2
3
4
5
6
7
8
9
public static void doSomethingToCommands(List<? extends Command> commands) {
    commands.forEach(this::doSomething);
}

List<FooCommand> fooCommands = getFooCommands();
doSomethingToComamnds(fooCommands); // Allowed

List<Command> commands = getRandomCommands();
doSomethingToComamnds(commands); // Not allowed.

既然你一次问了很多问题,让我扼要地说一下:

  • 当您实现要在不同对象上使用的数据结构、容器或算法而不必重写整个代码时,泛型是至关重要的。
  • 如果我在Java 5中引入了正确的泛型,在使用Java中的泛型之前,您仍然可以创建包含EDCOX1和10个对象的灵活数据结构,但是您必须将EDCOX1×11引用到最终类型,并在代码中的很多地方使用EDCOX1×12。因此引入了泛型来缓解这个问题并简化代码。
  • 正如niver已经说过的那样,?是一个通配符运算符,List将限制List包含扩展或实现类或接口Command以对对象接口实施约束的对象。

How is the generic type ? extends Command called, like is there a
name for it?

这些被称为通用通配符。

What exactly is this type? Is it a Command object?

它只用于在编译时检查变量赋值。在运行时,泛型被删除并解释为List myVar = client.performAction(actionParams);

Does this mean that it only accepts classes extending Command?

你只能说List myVar = client.performAction(actionParams);,它会接受任何类型为Command或命令子类的对象。

List myVar控制变量赋值。因此,您只能将以下泛型分配给myVar。假设子命令扩展了命令,则以下分配都是有效的:

1
2
myVar = new ArrayList<SubCommand>
myVar = new ArrayList<Command>

当用通配符generic声明集合时,它也可以成为只读的。例如,myVar.add(new SubCommand());将导致编译器错误。

What is my advantage using this sort of construct?

最初Java不包括泛型。它在Java 5中被添加以允许Java集合的类型限制,如EDCOX1、7、EDCOX1、8、EDCOX1、9、等等。