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

Posted

技术标签:

【中文标题】多态性在 Java 中的方法参数中不起作用【英文标题】:Polymorphism doesn't work in method arguments in Java 【发布时间】:2021-11-03 13:41:33 【问题描述】:

我写了以下代码:

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();

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

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

【问题讨论】:

【参考方案1】:

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

另请参阅:Static Binding and Dynamic Binding

【讨论】:

动态绑定的别称是双重调度还是有不同的概念? 我觉得double dispatch不一样,但是double dispatch一般也会实现动态绑定,所以经常一起看到。 Double Dispatch 可以使用访问者模式在 Java 中实现,这是必要的,因为 Java 是一种静态类型的语言。【参考方案2】:

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

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

我们可以争辩说,在这种情况下,Plane 类型的引用包含 Airbus 类型实例是非常清楚的。然而事情并没有那么简单,因为 Airbus 实例本身可能是一个方法参数,它可以保存任何子类实例或 Airbus 实例本身。

唯一安全的赌注是不通过父链进行解析并获取实际引用变量类型的面值的引用。另一种方法是实现与覆盖相同的方法重载,并使用对象的运行时绑定进行解析。我不知道为什么不这样做,因为它会使方法重载和覆盖更加统一。

以下是来自JLS Overloading的引用

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

【讨论】:

【参考方案3】:

Java 重载是编译时多态性。因此,当您将planeAirbus 声明为Plane 时,它将调用fly(Plane)

事实上,Main 类不应该知道 PlaneAirbus 会飞。 更好的设计:

public interface Flyable
  void fly();


public Plane implements Flyable
  void fly()
     //do something
  


 public Airbus implements Flyable
    void fly()
       //do something
    
 

然后在Main 类中

public static void main(String[] args) 
  Flyable plane = new Plane();
  plane.fly();

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

【讨论】:

【参考方案4】:

输出以下输出的原因:

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

因为它在执行重载,而重载是静态多态或编译时多态。在重载中,类可以有多个同名但参数类型不同的方法。在您的示例中,PlanePlane planeAirbus = new Airbus(); 的类型

【讨论】:

以上是关于多态性在 Java 中的方法参数中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

@Autowired 在方法参数中不起作用

带有 MultiPart 表单数据中的参数的图像上传在 Alamofire 4.0 中不起作用

为啥模板参数推导在 C++ 中不起作用?

Discord:命令参数中的撇号在 C# 中不起作用

Java 中的 FileLock 在 Docker 挂载卷中不起作用

Visual Studio 自定义代码片段在方法定义的参数列表中不起作用