3

Let's take this simple Java code:

public class Animal {
  public void eat() {
    System.out.println("Generic Animal Eating Generically");
   }
}

public class Horse extends Animal {
  public void eat() {
    System.out.println("Horse eating hay ");
  }

  public void eat(String s) {
    System.out.println("Horse eating " + s);
  }
}

I'm trying to figure out which version of the three eat() methods will run. Now, when I type

Animal a = new Animal();
a.eat();

The output is "Generic Animal Eating Generically", which is completely understandable.

The same thing happens when I type:

Horse h = new Horse();
h.eat();

The output is "Horse eating hay", which is, again, completely logical.

Here's where it gets confusing for me though. When I type:

Animal ah = new Horse();
ah.eat();

I get:

Horse eating hay

I expected the compiler to invoke the eat() method from the Animal class reference, not the Horse object reference.

So my question is, how can I know for sure which method the compiler is going to invoke when I have a generic reference variable types referring to an object type (like this one: Animal horse = new Horse();

Cœur
  • 37,241
  • 25
  • 195
  • 267
Jesse James
  • 1,203
  • 5
  • 22
  • 39

4 Answers4

3

I expected the compiler to invoke the eat() method from the Animal class reference, not the Horse object reference.

Let's correct this statement first. The variable ah is a reference of type Animal and the statement new Horse() creates an instance of type Horse and assigns it to an Animal reference.

Now that the terminologies are clear, this behavior is expected and is termed as runtype-polymorphism or dynamic method dispatch. At compile time, eat() is resolved based on the reference type which is of type Animal, but at runtime, the method that will be called is based on the instance type which is Horse.

how can I know for sure which method the compiler is going to invoke when I have a generic reference variable types referring to an object type

You could follow these simple steps :

  1. Check the method being called. ah.eat() is calling the method eat.
  2. See if a method with the exact same signature (with the exception being return type covariance) is present in both the parent and child class. (method overriden or not?)
  3. Check the reference type. In Animal ah = new Horse(), the reference type is Animal that is the parent class
  4. Check the instance type. In Animal ah = new Horse(), the instance type is Horse which is the child class.

If all the above conditions are satisfied, you are looking at runtype polymorphism and the method from the child class will be called. In any other scenario, the method to be called will be resolved based on the reference type.

It would also pay to understand that a child class inherits methods from its parents. Lets say that you delete the public void eat() method from Horse class, you are no longer Overrding the eat() method; however, the public void eat(String s) method in Horse is still said to Overload the inherited eat method from Animal. Next, lets add a public void eat(String s) method in Animal. With this addition, you are now Overloading the eat method in Animal and Overrding it in Horse class. No matter how you change the code, the 4 steps mentioned above will always help you decide which method will be called.

Chetan Kinger
  • 15,069
  • 6
  • 45
  • 82
  • Your post is really helpful, but could you please answer these two questions. 1) From condition (2), what if I have an overloaded method (instead of an overridden method ) in the parent and child class? Will then the reference decide which method to invoke? And my second question what other possible scenarios (from your last sentence) can we have? Thanks a lot and sorry for burderning you with my questions. (I chose your answer as the best btw). – Jesse James Jan 21 '17 at 08:36
  • Never mind. I just found out that the reference type determines which overloaded method to invoke. But when we have an overridden method, it's the object that decides which one. Thanks a lot and sorry for you trouble. – Jesse James Jan 21 '17 at 08:41
  • @JesseJames Added a new paragraph to make things a little more confusing. You could test out the 4 steps that I mentioned with the changes suggested in the paragraph and you will see that they always hold true :) – Chetan Kinger Jan 21 '17 at 08:56
  • Can the downvoter please leave a comment and explain what is wrong with my answer. Please do not indulge in hate votes on *Stack Overflow* as it is against the etiquette of this website. Let's have a civilized discussion on this like you would in "real life"? – Chetan Kinger Feb 15 '17 at 06:27
2

This is called dynamic binding in Java. The explicite object type is used not the reference type.

It is not possible to call the overriden super method and the overriding method using a single method, see: How to call the overridden method of a superclass. You could add a method to your horse, which delegates the call to the animal like:

public class Horse extends Animal {
    public void animalEat() {
        super.eat();
    }

    public void eat() {
        System.out.println("Horse eating hay ");
    }
}
Community
  • 1
  • 1
wake-0
  • 3,918
  • 5
  • 28
  • 45
  • *It is not possible to call the overriden super method*. I don't completely follow this. Why would you need a seperate `animalEat` method when you can say `super.eat()` inside the `eat` method itself? – Chetan Kinger Jan 21 '17 at 08:26
  • @CKing you can for sure implement it in the overriden method but then you call always the super, maybe you don't want that. Normally when you want to call the overriden method (without the super in it) you implement it. For the rare cases where you need access to the super method you can add the `animalEat` method - so it is a kind of enhancement. – wake-0 Jan 21 '17 at 08:41
  • I get what you are saying. But the statement *It is not possible to call the overriden super method* is incorrect because it is always possible to do this through `super`. You probably mean that it is not possible to call the overriden super method and the overriding method using a single method. – Chetan Kinger Jan 21 '17 at 08:55
1

This is happens because of method overriding. In method overriding, the reference type does not matter, it is the object type that matters. Animal ah is simply a reference to the object and the actual object is of type Horse. So, Horse's method will be called instead of reference type Animal's method.

birraa
  • 430
  • 1
  • 4
  • 15
0

Ohkay, new keyword will create instance of given class...

new Horse();

Now Horse class already is a child of Animal. So following will be instantiate.

public void eat() {
  System.out.println("Horse eating hay ");
}

Now you are trying to store that object in Animal's object. It means Object Of Horse is stored in Animal's Object.

Animal ah = new Horse();

So in Animal's object member of Horse is already stored. That is the reason that compiler is printing child class method values.