多态性在 Java 的方法参数中不起作用

2022-09-03 02:13:51

我写了以下代码:

class Plane {}
class Airbus extends Plane {}

public class Main {

    void fly(Plane p) {
        System.out.println("I'm in a plane");
    }

    void fly(Airbus a) {
        System.out.println("I'm in the best Airbus!");
    }

    public static void main(String[] args) {

        Main m = new Main();

        Plane plane = new Plane();
        m.fly(plane);

        Airbus airbus = new Airbus();
        m.fly(airbus);

        Plane planeAirbus = new Airbus();
        m.fly(planeAirbus);

    }
}

结果是:

I'm in a plane
I'm in the best Airbus!
I'm in a plane

不出所料,前两个调用分别给出和。I'm in a planeI'm in the best Airbus!

Plane planeAirbus = new Airbus();

该方法将此对象视为飞机,即使实际对象是空中客车。即使我添加到 ,也没有任何变化,上次调用的结果仍然是abstractclass PlaneI'm in a plane

所以问题是为什么多态性在方法参数和调用中不起作用?这有什么目的吗?它是如何工作的?


答案 1

这里的问题是Java不支持方法参数的动态绑定。你看到的是静态绑定,即在编译时选择要调用的方法的重载。

另请参见:静态绑定和动态绑定


答案 2

方法重载类型多态性是在Java中的编译时确定的。

这意味着Java必须从它们表示的引用类型中推断出方法参数的类型,因为它不知道它们在编译时持有的对象类型。

我们可以争辩说,在这种情况下,很明显,Plane类型的引用持有空中客车类型实例。然而,这并不那么简单,因为空中客车实例本身可能是一个方法参数,它可以容纳任何子类实例或空中客车实例本身。

唯一安全的赌注是不通过父链进行解析,并获取其面值的引用,即实际的参考变量类型。执行此操作的另一种方法可能是实现与重写相同的方法重载,并使用对象的运行时绑定进行解析。我不知道为什么它没有以这种方式完成,因为它会使方法重载和覆盖更加统一。

以下是JLS重载的参考

调用方法时 (§15.12),在编译时将使用实际参数(以及任何显式类型参数)的数量和参数的编译时类型来确定将被调用的方法的签名 (§15.12.2)。如果要调用的方法是实例方法,则将在运行时使用动态方法查找 (§15.12.4) 确定要调用的实际方法。