使用 super 的方法可以获取对象的子类型并将其添加到列表中吗?

Can a method using super take a subtype of an object and add it to the list?

所以下面的函数应该是一个接受 Car 对象并将其添加到 Col<> 对象的函数。

void insertCar(List<? super Car> c, Car x) {
   c.add(x)
}

问题是将 Nissan 类型的变量 n 对象添加到 List<Car> 类型的变量 c 是否可行(即 insertCar(c, n))

答案是肯定的,但我不确定为什么。我认为添加对象 Car 的子类型是不可能的,因为使用了 super。它只会采用 Car 类型或 Car.

的任何超类型

有人能听懂吗?

编辑是不是...

如果 List<> 本身是传入的其他子类型,我将无法添加 Nissan?例如,如果 List<? super Car> 实际上是 List<? super Ford>

下面似乎有相互矛盾的答案,但这是为考试复习提供的问题,因此非常确定所提供的问题和答案是正确的。这只是我对它的理解,我不确定。

The answer is yes but I'm not sure why. I thought adding subtypes of an object Car would not be possible because of the use of super. That it would only take types of type Car or any supertype of Car.

任何超级 class 引用都可以保存其实例或其任何子 class 实例的对象,因此这是完全合法的。

您的子类型对象将至少具有 Car 对象的所有属性和方法,通常还有更多自己的方法。如果将 Nissan 对象分配给 Car 变量,则可以调用 Car 变量的所有方法。相反,将 Car 实例分配给 Nissan 对象是不可能的,因为 Nissan 对象可能拥有比 Car 实例更多的方法。

The question asked whether if adding a variable n of type Nissan object to a variable c of type List<Car> would work (ie. insertCar(c, n))

把你的话写成代码,我相信这就是你所说的:

public class GenericCheck {

    static void insertCar(List<? super Car> c, Car x) {

        c.add(x);
    }

    public static void main(String[] args) {

        List<Car> c = new ArrayList<>();
        Nissan n = new Nissan();
        insertCar(c, n);
    }
}

class Car {}

class Nissan extends Car {}

是的。这很好,但是需要完成 3 种类型检查:

  1. List<Car>insertCarList<? super Car> 的第一个有效参数吗? ,它是一个子类型,例如见here
  2. NissaninsertCarCar 的第二个有效参数吗? ,它是一个子类型。
  3. 调用c.add(x)有效吗? ,参数x必须是Car类型或List<? super Car> c定义的Car的超类型,它是这样的根据定义 Car x.

那么为什么会令人困惑呢?因为您将 Nissan 类型传递给第二个参数,并且 认为 现在第 3 点将中断,因为 x 不是 Car 类型或Car 的超类型(它是一个子类型)。发生的事情是 Nissan 被升级为 Car,这是一个可行的论点。

请记住,要知道调用 c.add(x) 是否有效,您只需查看 cx 的定义(在方法的参数列表中)。这确保只要使用有效参数调用该方法,调用 c.add(x) 就是有效的。此检查与调用 insertCar 时的(两个)类型检查无关。这些是我们上面执行的 3 个检查。

编辑: 那么为什么 insertCar(List<? super Nissan> c, Car x) 不起作用?

因为类型擦除。在编译期间,编译器会擦除所有类型参数并用其第一个绑定替换每个参数(请参阅 here)。你得到的是 List<Nissan>add(Car) 的请求。但这无法编译,因为 Car 不是 Nissan.

的子类型

List<? super Car> 的第一种情况下,键入擦除将导致 List<Car>,然后 add(Car) 有效。

我认为最能消除您的困惑的是认识到泛型提供 仅编译时 检查。正如我在代码块上方提到的

static void insertCar(List<? super Car /* or Nissan */> c, Car x) {

    c.add(x);
}

无论在运行时调用该方法的参数是什么,都必须进行编译。这意味着第一个方法参数中的泛型与您传递给第二个参数的类型无关:insertCar(..., Car)insertCar(..., Nissan) 不会影响 c.add(x) 的可编译性。给定的参数被转换(向上转换)为方法的参数类型Car,这与方法的内容无关。