【问题标题】:Calling a method of a randomly generated subclass from its superclass从超类调用随机生成的子类的方法
【发布时间】:2017-08-02 16:58:19
【问题描述】:

我有以下完全抽象的场景,我正在尝试使用 OOP 概念将它们组合在一起,但我正在努力解决如何正确地做到这一点。

我有 3 种类型的汽车。这辆车只能做两件事——开车(1公里)和停车。所有汽车都可以以相同的方式驱动和停止。因此,在 OOP 世界中,我会将其表示为:

class Car{
    drive(){...}
    stop(){...}
}

这三种汽车是CarA、CarB和CarC,它们都是这个父类Car的子类。因此,让我们以其中一个为例,比如 CarA,它看起来像:

class CarA extends Car{}

到目前为止,一切都很好。现在这是我的任务。我在 Car 类型的数组中有大量不同类型的汽车(这是可能的,因为 Car 类是所有类型汽车的父级)。我有一个函数可以挑选出一辆随机汽车(使用一个随机生成的整数作为索引的范围内),称为 getRandomCar())。一旦我得到一辆随机的汽车,我的工作就是驾驶汽车(根据 Car 父类中定义的函数发生 1 公里)然后停下汽车。所以我做了类似的事情:

Car car = (Car) getRandomCar(cars); //cars is the Car array with all the different cars
car.drive(); //car is driven for 1 km
car.stop(); //car is stopped.

这是可行的,因为我得到的随机汽车总是在将其存储到名为 car 的 Car 引用变量之前对其父类进行类型转换。当我调用 car.drive() 时,它会调用 Car 类的方法 drive()。

现在假设其中一辆汽车(例如 CarA)的驾驶方式略有不同。因此,在 CarA 的类定义中,我将重载方法 drive()。

现在我的问题是,在上述情况下,如果我得到的随机汽车之一是 CarA 类型,那么 car.drive() 调用父方法而不是子方法。但是 CarA 的驾驶方式与其他汽车不同,这就是它有一个重载方法的原因。

所以在这种情况下,除了在调用 car.drive() 之前专门放置一个 if 条件并检查随机汽车的类型是否是 CarA 的类型(这可能很乏味)之外,有没有一种好方法可以轻松调用 CarA 自己的方法过程,如果说所有汽车都有自己的驱动方法)。在 OOP 世界中是否有这样一个场景的良好而优雅的实现?

【问题讨论】:

  • 你错了,即使你的变量声明的类型只是Car,也会调用来自CarA的重载的drive方法。但是,CarA 可以根据需要使用super.drive() 调用来自Car 的方法
  • 阅读Dynamic Method Dispatch or Runtime Polymorphism in Java。您声称 是错误的......如果我得到的随机汽车之一是 CarA 类型,那么 car.drive() 调用父方法而不是子方法....
  • @ArvindSasikumar 没错。如果没有,它将使重载完全无用。此外,它不仅适用于 Java,而且适用于所有 OOP 语言
  • 这个场景是 OOP 世界的重点之一。您的演员 (Car car = (Car) getRandomCar(cars)) 没有用:CarA 不需要转换为 Car,因为它已经是 Car。您的声明then car.drive() calls the parent method 不正确。最派生的方法被调用,在你的情况下是CarA。为了澄清你的想法,试着把Car做成一个接口,然后实现DefaultCar,然后导出CarA、B和C。
  • @ArvindSasikumar 这种对 Java 多态性的困惑对于开始使用 Java 的 C++ 程序员来说很常见。如果您是这样的人,那么在 Java 中 virtual 是默认值。 C++ 中最接近非虚方法的是final 方法,但也不一样。

标签: java oop


【解决方案1】:

在 Java 中,方法在编译时检查,但在运行时绑定。当您将 car 变量声明为

Car car = (Car) getRandomCar(cars);

car 现在是CarA 的实例,引用类型为Car。据编译器所知,carCar

当您调用car.drive() 时,编译器会根据Car 类的定义检查该方法。 Car 确实包含一个名为drive() 的方法,因此编译成功。当程序实际运行时,JVM 到达该语句并在实际对象上调用drive() 。由于car 实际上是CarA,所以调用CarAdrive() 版本。对于不包含自己定义的CarBCarC,自动继承Cardrive() 版本。


其他一些注意事项(我注意到的事情和在 cmets 中提到的事情):

  1. 您在此处描述的场景称为覆盖,而不是重载。重载是使多个方法具有相同的名称和返回类型,但参数不同。覆盖是通过在子类中创建相同的方法来替换超类的功能。

  2. 在您的car 分配中,您不需要强制转换为Car。由于getRandomCar() 可以返回任何类型的Car,所以它的返回类型一定是Carcar 也被声明为 Car 类型,因此不需要强制转换。

【讨论】:

    猜你喜欢
    • 2015-04-04
    • 2011-11-22
    • 2011-10-24
    • 2012-04-18
    • 1970-01-01
    相关资源
    最近更新 更多