0

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; Bicycle being the superclass, and MountainBike and RoadBike being 2 subclasses.

It shows how the 2 subclasses override a method "printDescription" declared in Bicycle, by declaring different versions of it.

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.

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"? 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?

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();
      }
}
softwarelover
  • 1,009
  • 1
  • 10
  • 22
  • 1
    Are you a C++ developer? Your background might be important in the explanation you will get. – amit Oct 03 '12 at 13:02
  • Link to mentioned tutorial: http://docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html – Duncan Jones Oct 03 '12 at 13:02
  • Yes, I am a C++ developer. And, in the back of my mind I was thinking about the "virtual" keyword used for run-time polymorphism in C++. – softwarelover Oct 03 '12 at 13:06
  • @softwarelover: When asking a question about a principle of a language, try to give a quick brief on your background, you might find answers are more focused on **your** confusion, and thus more helpful. – amit Oct 03 '12 at 13:13

6 Answers6

4

How is run-time polymorphism achieved unless printDescription() in Bicycle is declared "abstract"?

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. Anytime a method is invoked on an instance, the type of the instance at runtime is used to determine which method implementation to use.

hvgotcodes
  • 118,147
  • 33
  • 203
  • 236
  • hello hvgotcodes, by "anytime" do you mean when there are overridden methods that exist in different classes? Because, overloaded method calls are resolved at compile-time. – softwarelover Oct 03 '12 at 13:15
  • ok, I understand now, as heisenbug pointed out below about Java objects not existing on the stack... – softwarelover Oct 03 '12 at 13:42
2

In Java all methods are bind at run-time (that's what you can achieve in C++ declaring a method virtual).

So the JVM can always dispatch the method correctly.

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++). This actually force the JVM to always check the run-time type of an object reference.

Heisenbug
  • 38,762
  • 28
  • 132
  • 190
  • This statement is wrong. A method is abstract by default only in interface. Did you mean *virtual*? – amit Oct 03 '12 at 13:03
  • I still don't like this answer (though it is not wrong anymore), since the *virtual* concept is not defined in java. A better statement will be: "In java, the default invokation of methods is always *dynamic* (dispatch/binding)" – amit Oct 03 '12 at 13:06
  • If I may join amit and heisenbug, would you please comment on the situation where a method is not overridden but, rather, overloaded? Isn't the method invocation then resolved at compile-time? – softwarelover Oct 03 '12 at 13:23
  • no. the overloaded methods are 2 different methods despite they share the same name. You can't still achieve static binding in Java, because nothing is static. – Heisenbug Oct 03 '12 at 13:26
  • This doesn't really have anything to do with stack-vs-heap, though. It's really about whether you need a vtable -- in Java, you always do at the conceptual level. Note that the JIT will often notice that a given code path always gets the same implementation, in which case it'll replace the vtable with a quick check for type and then a non-dispatched call. – yshavit Oct 03 '12 at 13:33
  • @yshavit: I was referring to the fact that in C++ you can have static binding because you aren't always dealing with references. Thus if you allocate an object on the stack, the compiler can bind the method at compile time. – Heisenbug Oct 03 '12 at 13:39
  • @Heisenbug I understand, but in C++ you can have static binding with pointers, too. It just means that the value of the `this` pointer is unknown -- but the location of the method's code in memory is known, regardless of where the object is. Basically it's a question of whether the program counter always jumps to the same place, or whether it needs to look at some metadata to know where to jump to. – yshavit Oct 03 '12 at 13:54
2

virtual is a keyword in many languages that means "this method can be overridden by a subclass". Java does not have that keyword but instead all non static member methods are virtual and possible to override.

abstract is the same as virtual, except it tells the compiler that the base class does not have a definition for the method. 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. It's virtual by default and therefor overridable by the subclass, so there is no need for a keyword to indicate that.

Joachim Isaksson
  • 176,943
  • 25
  • 281
  • 294
1

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.

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..

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..

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
0

This is not C++. In Java, you always know the real class of each instance so, when printDescription() is invoked, the definition of that class is used. 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).

SJuan76
  • 24,532
  • 6
  • 47
  • 87
0

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

...

wxyz
  • 709
  • 3
  • 8