没有“抽象”的Java中的运行时多态性?

我正在阅读官方的Oracle教程,其中介绍了多态性的概念,以及3个类的类层次结构的示例; 自行车是超类,MountainBike和RoadBike是2个子类。

它显示了2个子类如何重写在Bicycle中声明的方法“printDescription”,通过声明它的不同版本。

最后,在最后,教程提到Java虚拟机(JVM)为每个变量中引用的对象调用适当的方法。

但是,关于多态性的教程没有提到“抽象”类和方法的概念。 除非将Bicycle中的printDescription()声明为“abstract”,否则如何实现运行时多态性? 我的意思是,在这个例子中,基于什么提示,编译器决定在编译时不将方法调用绑定到引用类型,并认为它应该留给JVM在运行时处理?

以下是使用的示例:

public class Bicycle { public int cadence; public int gear; public int speed; public Bicycle(int startCadence, int startSpeed, int startGear) { gear = startGear; cadence = startCadence; speed = startSpeed; } public void setCadence(int newValue) { cadence = newValue; } public void setGear(int newValue) { gear = newValue; } public void applyBrake(int decrement) { speed -= decrement; } public void speedUp(int increment) { speed += increment; } public void printDescription(){ System.out.println("\nBike is " + "in gear " + this.gear + " with a cadence of " + this.cadence + " and travelling at a speed of " + this.speed + ". "); } } public class MountainBike extends Bicycle { private String suspension; public MountainBike( int startCadence, int startSpeed, int startGear, String suspensionType){ super(startCadence, startSpeed, startGear); this.setSuspension(suspensionType); } public String getSuspension(){ return this.suspension; } public void setSuspension(String suspensionType) { this.suspension = suspensionType; } public void printDescription() { super.printDescription(); System.out.println("The " + "MountainBike has a" + getSuspension() + " suspension."); } } public class RoadBike extends Bicycle{ private int tireWidth; public RoadBike(int startCadence, int startSpeed, int startGear, int newTireWidth){ super(startCadence, startSpeed, startGear); this.setTireWidth(newTireWidth); } public int getTireWidth(){ return this.tireWidth; } public void setTireWidth(int newTireWidth){ this.tireWidth = newTireWidth; } public void printDescription(){ super.printDescription(); System.out.println("The RoadBike" " has " + getTireWidth() + " MM tires."); } } public class TestBikes { public static void main(String[] args){ Bicycle bike01, bike02, bike03; bike01 = new Bicycle(20, 10, 1); bike02 = new MountainBike(20, 10, 5, "Dual"); bike03 = new RoadBike(40, 20, 8, 23); bike01.printDescription(); bike02.printDescription(); bike03.printDescription(); } } 

除非将Bicycle中的printDescription()声明为“abstract”,否则如何实现运行时多态性?

为什么你认为抽象类会改变什么? 抽象类做了两件事

  1. 允许程序员声明一个本身不能实例化的类,强制子类化,和
  2. 允许程序员通过声明方法abstract来强制子类提供方法的实现。

请注意,第2点并不意味着除非在基类上将方法声明为抽象,否则多态将不起作用; 相反,它为开发人员提供了强制子类提供实现的机会,这在子类化不需要任何抽象用法的场景中是不需要的。

而已。 换句话说,抽象概念称赞Java的多态性 – 它是一种语言特性,但与Java在运行时用于调用方法的动态调度没有任何关系。 无论何时在实例上调用方法,运行时的实例类型都将用于确定要使用的方法实现。

virtual是许多语言中的关键字,意味着“此方法可以被子类覆盖”。 Java没有该关键字,而是所有非静态成员方法都是虚拟的并且可以覆盖

abstract与virtual相同,只是它告诉编译器基类没有方法的定义。 如果没有基类执行的有用函数,它有时很有用,但它绝不需要能够覆盖基类方法。

在您的情况下,printDescription方法对基类有一个有用的定义,因此不需要将其声明为抽象。 默认情况下它是虚拟的,因此可以由子类覆盖,因此不需要关键字来指示它。

在Java中,所有方法都在运行时绑定(这是您在C ++中声明方法虚拟的方法)。

因此JVM总是可以正确地调度方法。

实际上,Java中的方法绑定永远不会是静态的,因为您总是处理对象的引用(不能在堆栈上分配对象,比如C ++)。 这实际上强制JVM始终检查对象引用的运行时类型。

没有必要声明该方法是抽象的。在运行时多态性中,根据基类引用指向的类实例调用适当的派生类方法。

请考虑以下示例: –

 class A { public void doSomething() { } } class B extends A { public void doSomething() { System.out.println("In B") } } public class Test { public static void main(String args[]) { A obj = new B(); // Base class reference and derived class object. obj.doSomething(); // Calls derived class B's method and prints `In B` } } 

引用你所读的陈述: –

本教程提到Java虚拟机(JVM)为每个变量中引用的对象调用适当的方法。

要certificate上述陈述的合理性,请参阅上面的示例。 你的B类方法被调用,因为你的基类引用obj指向派生类B's实例。

始终在编译时检查指向对象的引用类型,而在运行时检查该引用指向的对象类型。

因此,将在运行时 决定调用哪个方法 无论您的基类方法是否abstract ,都会调用适当的派生类方法。

这不是C ++。 在Java中,您始终知道每个实例的真实类,因此,当调用printDescription()时,将使用该类的定义。 但是,您只能使用实例引用中可用的方法(因此,如果您在类RoadBike定义方法getMPH()并使用Bike变量处理该类的实例,则编译器将提示错误,如果您打算用它)。

我想这段代码:

 bike01 = new Bicycle(20, 10, 1); bike02 = new MountainBike(20, 10, 5, "Dual"); bike03 = new RoadBike(40, 20, 8, 23); bike01.printDescription(); bike02.printDescription(); bike03.printDescription(); 

不是运行时多态的最好例子,因为即使在编译时也知道所有事实(调用方法)。 但是如果你把它改成:

 Random r = new Random(); if(r.nextInt(2)%2==0) { bike01 = new Bicycle(20, 10, 1) } else { bike01 = new MountainBike(20, 10, 5, "Dual"); } // only at run-time the right function to call is known bike01.printDescription();