17

As per the rule, while overriding a method in subclass, parameters cannot be changed and have to be the same as in the super class. What if we pass subclass of parameter while overriding method ? Will it be called as overloading or overriding?

Based on my query I have written some code below.
I was expecting the output as "Dog eats Flesh Food" but to my surprise the output is "Animal eats Flesh Food" Will appreciate if someone can explain how does Animal method gets called when the object assigned is of type Dog ?

    class Food {
        public String toString(){
            return "Normal Food";
        }
    }

    class Flesh extends Food {
        public String toString(){
            return "Flesh Food";
        }
    }

    class Animal {
        public void eat(Food food){
            System.out.println("Animal eats "+ food);
        }
    }

    class Dog extends Animal{

        public void eat(Flesh flesh){
            System.out.println("Dog eats "+ flesh);
        }
    }

    public class MyUtil {

        public static void main(String[] args) {

            Animal animal = new Dog(); 

            Flesh flesh = new Flesh();

            animal.eat(flesh);
        }
    }
kgui
  • 4,015
  • 5
  • 41
  • 53
SD Shaw
  • 181
  • 1
  • 1
  • 9
  • 1
    You've already written the code - try it and find out what the compiler says. – childofsoong Aug 04 '15 at 23:13
  • see "visitor pattern" – ZhongYu Aug 04 '15 at 23:14
  • The compiler does not complain anything. Actually the program runs that is why I am able to see the output but its weird. If I explicitly annotate the method as @Override, compiler complains, that means it is not overriding. You will also get surprised. Just copy the code in your eclipse, guess the output and then execute and verify the actual output. – SD Shaw Aug 04 '15 at 23:15
  • 1
    If I was to do `animal.eat(new Food());` in your code, what would you expect to happen? Why? – Sotirios Delimanolis Aug 04 '15 at 23:18
  • possible duplicate of [Overloaded method selection based on the parameter's real type](http://stackoverflow.com/questions/1572322/overloaded-method-selection-based-on-the-parameters-real-type) – childofsoong Aug 04 '15 at 23:21
  • Put @Override on it and see what the compiler does. No need to waste time asking questions about it, and getting possibly wrong answers, or none. The compiler's opinion is the only one that counts. – user207421 Aug 05 '15 at 00:52

7 Answers7

17

The method eat in Dog does not override the method eat in Animal. This is because the arguments are different (one requires Flesh, the other requires Food).

The eat methods are overloads.

Choosing between overloads takes place at compile time, not runtime. It is not based on the actual class of the object on which the method is invoked, but the compile-time type (how the variable is declared).

animal has compile-time type Animal. We know this because the declaration of the variable animal was Animal animal = .... The fact that it is actually a Dog is irrelevant - it is the version of eat in Animal that must be invoked.

On the other hand, the toString method in Flesh does override the toString method in Food.

When one method overrides another it is the actual class of the object that the method is invoked on that determines which version runs.

In the eat method of Animal, even though the argument has compile-time type Food, if you pass an instance of Flesh to it, it is the toString method in Flesh that will execute.

Therefore you get the message "Animal eats Flesh Food".

Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
5

To be able to dynamically load the subclass at runtime, the use of generics makes it work perfectly.

public class HelloWorld{

     public static void main(String []args){
        Animal animal = new Dog(); 

        Food flesh = new Flesh();

        animal.eat(flesh);
     }
}

 class Food {
        public String toString(){
            return "Normal Food";
        }
    }

    class Flesh extends Food {
        public String toString(){
            return "Flesh Food";
        }
    }

    class Animal<T extends Food> {
        public void eat(T food){
            System.out.println("Animal eats "+ food);
        }
    }

    class Dog extends Animal<Flesh>{
        
        public void eat(Flesh flesh){
            System.out.println("Dog eats "+ flesh);
        }
    }
Navin Kumar
  • 3,393
  • 3
  • 21
  • 46
Sunaina Goyal
  • 51
  • 1
  • 2
  • 1
    maybe add a paragraph about the fact, that the eat method is not overriden because of another paramter type in the question and why your solution would work – Dude Nov 24 '20 at 06:55
  • Welcome to SO, when answering a question don't just give the code. Add some info about the code and how you solved the problem. – theWellHopeErr Nov 24 '20 at 07:42
  • i dont know if this is right answer for this question or not, but helped me in solving my generic type issue similar to this, thank you – riaz7se Nov 17 '22 at 15:27
  • this is definitely the right answer! it is far better than all the answers above and saved us too. We used it for the generic interface we have for the Stripe callbacks handlers! – Mohamad Eghlima Jan 13 '23 at 02:36
3

You can use the @override annotation to inform the compiler that you are trying to override a method within the superclass.

e.g.

 class Dog extends Animal{

    @Override
    public void eat(Flesh flesh){
        System.out.println("Dog eats "+ flesh);
    }
 }

In your code, now the compiler will generate an error, because this method does not override eat, you need to have the same parameter type.

However, changing the Flesh parameter to Food type will resolve the problem:

class Dog extends Animal{

    @Override
    public void eat(Food food){
        System.out.println("Dog eats "+ food);
    }
}

Now you can do the following:

public class MyUtil {

    public static void main(String[] args) {

        Animal animal = new Dog(); 

        Food food = new Flesh();

        animal.eat(food);
    }
}
yas
  • 3,520
  • 4
  • 25
  • 38
2

No, we can not in this case you can check it by adding @Override above the eat method in Dog class so compilation error will appear like this :

@Override
public void eat(Flesh flesh){
   System.out.println("Dog eats "+ flesh);
}

Make sure that in override the parameters should be as parent class in type and order.

Ahmad Al-Kurdi
  • 2,248
  • 3
  • 23
  • 39
2

In java, Can we override a method by passing subclass of the parameter used in super class method?

NO.

Explanation with example :

Assume we can override a method by passing subclass of the parameter used in super class method.

class A {
    public void print(Oject obj) 
    {
      System.out.println(obj.toString());
     }
}

class B extends A {
    @Override
     public void print(String str) 
     {
        System.out.println(str.toString());
     }
}

class C extends A {
   @Override
   public void print(Integer in) {
      System.out.println(in.toString());
   }
}

class MainTest {
   public static void main(String args[]) 
   {
      Integer in = new Integer(3);
      A a = new B(); // developer by mistake types new B() instead of new C()
      a.print(in);  // compiler will allow this because class A has print method which can take any subclass of Object type(in this case any class because Object is superclass of all classes in java).
    }
}

But think what happens during runtime ?

During runtime, method is called on the actual object to which reference is pointing to. In our case it is pointing to class B. So JVM will look into class B for method execution and it would be a great shock to JVM because class B does not have a overriden method with signature public void print(Integer in) and it will fail at runtime.

The consequence of this is application will crash at runtime rather than compile time.

Overriding rules in Java :

  • Arguments must be the same & return types must be compatible
  • The method can't be less accessible

=============================

In your example what happens is :

During compile time : compiler makes sure that eat method can be called on reference type(which is Aniaml class). Since Animal class has a method public void eat(Food food) which can take any Food type and subclass of Food type as input parameter(polymorphism), compilation passes.

In general, compiler guarantees that particular method is callable for a specific reference type.

During run time : Actual method called is, most specific version of the method for that object type(Dog class).Since JVM is not able to find the overriden method i.e with the same method name and same argument type declared in sub class (Dog class), it checks its superclass(Animal class) and finds the method and executes.

public void eat(Food food) and public void eat(Flesh flesh) are not same from the perspective of overriding methods.

In general, when you call a method on an object reference, you're calling the most specific version of the method for that object type during runtime. But if it doesn't find one it will go up the hierarchy.Worst case Super class will definitely have the method implementation otherwise compilation wont pass.

Deepa
  • 41
  • 6
  • Well, it's not actually the case here: if compiler sees A::print, it will place bytecode for A::print invocation, it won't try to place invocation of B::print even if A a =new B(), because compiler never making guesses about types, it only solves equations. What IS the case here is just that method overriding another method just must have same argument types, otherwise it's not overriding, it's a new method – Semyon Danilov Nov 26 '18 at 22:28
1

When you do this: Animal animal = new Dog(); it's upcasting.

And you do not override the method

public void eat(Flesh flesh){
    System.out.println("Dog eats "+ flesh);
}

so it calls the method eat(Food food) of Animal

Michael Dodd
  • 10,102
  • 12
  • 51
  • 64
Jack Bu
  • 11
  • 4
0

in fact you can't @Override a method with a different signature, look at this example:

public class Clase1 {
    
    public void metodo1 (ClasePadre atr1) {
        System.out.println("Hola soy Clase1");
    }
}

public class Clase2 extends Clase1 {
    
    public void metodo1 (ClaseHija atr1) {
        System.out.println("Hola soy Clase2");
    }

}

public class ClasePadre {
    
    public void metodo1 () {
        System.out.println("Hola soy ClasePadre");
    }
}

public class ClaseHija extends ClasePadre {
    
    public void metodo1 () {
        System.out.println("Hola soy ClaseHija");
    }
}

When you does a main method and instanciate this Clase2 clase2 = new Clase2 (); you will see two different methods metodo1(ClaseHija atr1), and metodo2(ClasePadre atr1), there fore you are Overloading instead Overriding.