繁体   English   中英

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

[英]Run-time Polymorphism in Java without “abstract”?

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

它显示了这两个子类如何通过声明其不同版本来覆盖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();
      }
}

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

您为什么认为抽象类会改变任何东西? 抽象类做两件事

  1. 允许程序员声明一个无法实例化的类,强制子类化,以及
  2. 通过声明方法抽象,允许程序员强制子类提供方法的实现。

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

而已。 换句话说,抽象的概念使Java的多态性得到了补充-它是一种语言功能,但与Java在运行时用于调用方法的动态调度没有任何关系。 每当在实例上调用方法时,运行时实例的类型将用于确定要使用的方法实现。

在Java中,所有方法都在运行时绑定(这是在C ++中将方法声明为虚拟方法可以实现的功能)。

因此,JVM始终可以正确地分派该方法。

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

virtual是许多语言的关键字,表示“该方法可以被子类覆盖”。 Java没有该关键字,但是所有非静态成员方法都是虚拟的,可以覆盖

abstract与virtual相同,不同之处在于abstract告诉编译器基类没有该方法的定义。 有时,如果没有有用的函数可以执行基类,那么它很有用,但是并不需要覆盖基类方法。

在您的情况下,printDescription方法为基类提供了有用的定义,因此无需将其声明为抽象的。 默认情况下它是虚拟的,因此可以被子类覆盖,因此不需要关键字来表明这一点。

在运行时多态中,将根据基类引用所指向的类实例来调用适当的派生类方法。

考虑以下示例:-

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)为每个变量中引用的对象调用适当的方法。

为了证明以上陈述的合理性,请参见以上示例。 因为您的基类引用obj指向派生类B's实例,所以将调用您的B类方法。

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

因此,这些方法将被调用的决定 在运行时做出。 不管你的基类的方法是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();

...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM