我正在复习Oracle官方教程,其中以3个类的类层次结构为例介绍了多态性的概念;自行车是超类,山地自行车和公路自行车是2个子类。
它展示了两个子类如何通过声明Bicycle中声明的方法"printDescription"的不同版本来覆盖它
最后,在最后,本教程提到Java虚拟机(JVM)为每个变量中引用的对象调用适当的方法。
但是,多态性教程中没有提到"抽象"类和方法的概念。除非Bicycle中的printDescription()被声明为"抽象",否则如何实现运行时多态性?我的意思是,在这个例子中,编译器根据什么提示决定在编译时不将方法调用绑定到引用类型,并认为应该让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();
}
}
除非在自行车被宣布为"抽象"?
为什么你认为抽象类会改变任何东西?抽象类做两件主要事情
- 允许程序员声明一个本身不能实例化的类,强制进行子类化,以及
- 允许程序员通过声明方法抽象来强制子类提供方法的实现
请注意,第2点并不意味着多态性不会起作用,除非在基类上声明一个方法是抽象的;相反,它为开发人员提供了一个force子类以提供实现的机会,这在不涉及任何抽象用法的子类化场景中是不需要的。
就是这样。换句话说,抽象的概念补充了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)调用每个中引用的对象的适当方法变量
要证明上述语句的正确性,请参阅上面的示例。在那里,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();