简体   繁体   English

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

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

I was going over the official Oracle tutorial where it introduces the idea of polymorphism with the example of a class hierarchy of 3 classes; 我正在阅读Oracle官方教程,其中以3个类的类层次结构为例介绍了多态性的概念。 Bicycle being the superclass, and MountainBike and RoadBike being 2 subclasses. 自行车是超类,MountainBike和RoadBike是两个子类。

It shows how the 2 subclasses override a method "printDescription" declared in Bicycle, by declaring different versions of it. 它显示了这两个子类如何通过声明其不同版本来覆盖Bicycle中声明的方法“ printDescription”。

And finally, toward the end, the tutorial mentions the Java Virtual Machine (JVM) calls the appropriate method for the object that is referred to in each variable. 最后,在最后,本教程提到Java虚拟机(JVM)为每个变量中引用的对象调用适当的方法。

But, nowhere does the tutorial on polymorphism mention the concept of "abstract" classes and methods. 但是,关于多态的教程中没有任何地方提到“抽象”类和方法的概念。 How is run-time polymorphism achieved unless printDescription() in Bicycle is declared "abstract"? 除非在Bicycle中将printDescription()声明为“抽象”,否则如何实现运行时多态? I mean, given this example, based on what hints does the compiler decide not to bind method invocation to the reference type at compile time, and think that it should leave it for the JVM to deal with at run-time? 我的意思是,在给出此示例的情况下,基于什么提示,编译器是否决定在编译时不将方法调用绑定到引用类型,并认为应该让它在运行时让JVM处理?

Below is the example used: 下面是使用的示例:

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

How is run-time polymorphism achieved unless printDescription() in Bicycle is declared "abstract"? 除非在Bicycle中将printDescription()声明为“抽象”,否则如何实现运行时多态?

Why would you think abstract classes would change anything? 您为什么认为抽象类会改变任何东西? Abstract classes do 2 primary things 抽象类做两件事

  1. Allow the programmer to declare a class that cannot itself be instantiated, forcing subclassing, and 允许程序员声明一个无法实例化的类,强制子类化,以及
  2. Allow the programmer to force subclasses to provide implementations of methods, by declaring the method abstract. 通过声明方法抽象,允许程序员强制子类提供方法的实现。

Note that point 2 does not imply that polymorphism won't work unless a method is declared abstract on the base class; 注意,除非在基类上抽象地声明了一个方法,否则第二点并不意味着多态性将无法工作。 rather, it provides the developer an opportunity to force a subclass to provide an implementation, which is not required in subclassing scenarios that don't involve any abstract usage. 相反,它为开发人员提供了强制子类提供实现的机会,这在不涉及任何抽象用法的子类化场景中是不需要的。

That's it. 而已。 In other words, the notion of abstract compliments Java's polymorphism -- it is a language feature, but doesn't have anything to do with the dynamic dispatch Java uses at runtime to invoke methods. 换句话说,抽象的概念使Java的多态性得到了补充-它是一种语言功能,但与Java在运行时用于调用方法的动态调度没有任何关系。 Anytime a method is invoked on an instance, the type of the instance at runtime is used to determine which method implementation to use. 每当在实例上调用方法时,运行时实例的类型将用于确定要使用的方法实现。

In Java all methods are bind at run-time (that's what you can achieve in C++ declaring a method virtual). 在Java中,所有方法都在运行时绑定(这是在C ++中将方法声明为虚拟方法可以实现的功能)。

So the JVM can always dispatch the method correctly. 因此,JVM始终可以正确地分派该方法。

Actually the method binding in Java could never be static, because you are always dealing with references to objects (can't allocate an object on the stack, like C++). 实际上,Java中的方法绑定永远不会是静态的,因为您总是要处理对对象的引用(不能像C ++那样在堆栈上分配对象)。 This actually force the JVM to always check the run-time type of an object reference. 这实际上迫使JVM始终检查对象引用的运行时类型。

virtual is a keyword in many languages that means "this method can be overridden by a subclass". virtual是许多语言的关键字,表示“该方法可以被子类覆盖”。 Java does not have that keyword but instead all non static member methods are virtual and possible to override . Java没有该关键字,但是所有非静态成员方法都是虚拟的,可以覆盖

abstract is the same as virtual, except it tells the compiler that the base class does not have a definition for the method. abstract与virtual相同,不同之处在于abstract告诉编译器基类没有该方法的定义。 It's useful at times if there is no useful function for the base class to perform, but it's by no means needed to be able to override the base class method. 有时,如果没有有用的函数可以执行基类,那么它很有用,但是并不需要覆盖基类方法。

In your case, the printDescription method has a useful definition for the base class, so there's no need to declare it abstract. 在您的情况下,printDescription方法为基类提供了有用的定义,因此无需将其声明为抽象的。 It's virtual by default and therefor overridable by the subclass, so there is no need for a keyword to indicate that. 默认情况下它是虚拟的,因此可以被子类覆盖,因此不需要关键字来表明这一点。

There no need to declare that method abstract.. In runtime polymorphism, appropriate derived class method is invoked based on what class instance does the base class reference points to.. 在运行时多态中,将根据基类引用所指向的类实例来调用适当的派生类方法。

Consider the following example: - 考虑以下示例:-

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`
    }
}

To quote the statement that you read: - 引用您阅读的声明:-

the tutorial mentions the Java Virtual Machine (JVM) calls the appropriate method for the object that is referred to in each variable. 本教程提到Java虚拟机(JVM)为每个变量中引用的对象调用适当的方法。

To justify the above statement, see the above example. 为了证明以上陈述的合理性,请参见以上示例。 There your B class method is invoked because your base class reference obj is pointing derived class B's instance.. 因为您的基类引用obj指向派生类B's实例,所以将调用您的B类方法。

Type of the reference pointing the object is always checked at compile time, whereas, the type of object pointed by that reference is checked at runtime.. 始终在编译时检查指向该对象的引用的类型,而在运行时检查该引用所指向的对象的类型。

So, the decision of which method will be invoked is made at runtime. 因此,这些方法将被调用的决定 在运行时做出。 . Regardless of the whether your base class method is abstract or not , appropriate derived class method is invoked.. 不管你的基类的方法是abstract没有 ,适当的派生类的方法被调用..

This is not C++. 这不是C ++。 In Java, you always know the real class of each instance so, when printDescription() is invoked, the definition of that class is used. 在Java中,您总是知道每个实例的真实类,因此,在调用printDescription()时,将使用该类的定义。 Though, you can only use the methods available in the references to the instance (so, if you define a method getMPH() in class RoadBike and you handle an instance of that class with a Bike variable, the compiler will thow an error if you intend to use it). 不过,您只能使用实例引用中的可用方法(因此,如果您在RoadBike类中定义了方法getMPH() ,并且使用Bike变量处理该类的实例,则编译器将发出错误消息。打算使用它)。

I think this code: 我认为这段代码:

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

is not the best example of run-time polymorphism because all facts (methods to call) are known even at compile time. 这不是运行时多态性的最佳示例,因为即使在编译时,所有事实(调用方法)也是已知的。 But if you would change it to: 但是,如果您将其更改为:

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