Lucas Tulio
Lucas Tulio

Reputation: 227

Why doesn't Java allow me to perform this type of polymorphism/inheritance?

I am refactoring a huge if statement I have. One of the ways I found to improve it was by using polymorphism and inheritance. In a very simplified way, this is what I have in my code:

public abstract class Animal {  
    public abstract void doAction();  
}

public class Dog extends Animal {  
    public void doAction() {System.out.println("run");} 
}

public class Cat extends Animal {  
    public void doAction() {System.out.println("sleep");}
}

public class RunActions {  
    public void runAction(Dog d) {
        d.doAction();
    }

    public void runAction(Cat c) {
        c.doAction();
    }
}

public class Start {
    public static void main(String args[]) {
        Animal animal = new Dog();
        new RunActions().runAction(animal); // Problem!
    }
}

I know, I know. I could just call animal.doAction();. Or add a method in RunActions that receives Animal as a parameter.

But why doesn't the compiler allow me to call that last "runAction(animal)" line? Shouldn't the JVM figure out animal is an instance of Dog in runtime?

Is there an specific reason why it doesn't allow me to do that?

EDIT: Forgot to make Dog and Cat extend Animal. Fixed.

Upvotes: 2

Views: 466

Answers (6)

Bohemian
Bohemian

Reputation: 425418

The problem is not all Animals may be Cats or Dogs. Consider:

public class Fish implements Animal{  
    public void doAction() {System.out.println("swim");  
}

What would you expect your RunActions class to do?

That's why the compiler is complaining.


There are a few approaches you can use to make your situation work. The simplest is to have one method that accepts Animal and use a series of instanceof tests to figure out what you want to do with each specific subclass of Animal.

Upvotes: 1

stinepike
stinepike

Reputation: 54742

Better do following

public interface Animal {  
    public void doAction();  
}

public class Dog implements Animal{  
    public void doAction() {System.out.println("run");  
}

public class Cat implements Animal{  
    public void doAction() {System.out.println("sleep");  
}

public class RunActions {  
    public void runAction(Animal d) {
        d.doAction();
    }
}

public class Start {
    public static void main(String args[]) {
        Animal animal = new Dog();
        new RunActions().runAction(animal);
    }
}

Upvotes: 0

Ankur Trapasiya
Ankur Trapasiya

Reputation: 2200

Firstly, you are not extending Animal in Dog and Cat. So do that first.

In inheritance ISA principal gets followed.

so for example

public class Dog extends Animal

here Dog extends Animal so that DOG IS ANIMAL but the reverse is not true ANIMAL CANNOT BE NECESSARILY A DOG. IT CAN BE CAT ALSO IN YOUR CASE.

So when you pass a reference of animal to the method which accepts a DOG or a CAT assignment would be something like

Dog d=animal;

which is read as animal is DOG and that is not true.

So compiler will not allow you to do that.

And regarding why Java doesn't allow that thing to be done is to achieve the features that it is capable of achieving.

Say for example, Java allows you to pass the animal object to the method and allows you to execute the method.

so in that case

Animal animal=new Dog();
Dog d= animal;
d.doSomething(); // let's say java allowed this no problem since animal is DOG.

BUT,

Animal animal=new Horse();
Dog d= animal;
d.doSomething(); // Can you imagine what will happen in this case?

So to avoid such situaltion java is intelligent enough to stop you when you are doing wrong. Hope this clears your doubt and help you to understand this.

Upvotes: -1

jlordo
jlordo

Reputation: 37843

The compiler can't guarantee that there is an appropriate method at runtime.

You have a method that takes a Cat and you have a method that takes a Dog. You are trying to pass an Animal variable that references a Dog. What if would reference an Elephant? Then there would be no suitable method at runtime. That's why it won't let you compile.

Animal animal = new Elephant();
new RunActions().runAction(animal); // real problem now!

Upvotes: 5

Marko Topolnik
Marko Topolnik

Reputation: 200296

The main underlying concept that makes what you want impossible is that Java is a single-dispatch language, just like almost all other languages called "OOP". What this means is that the runtime decision which method to call takes into account only the first method argument, which is syntactically placed before the dot, the one whose value will be bound to the this special variable.

You may also wonder why single dispatch is used in most languages... this has to do with the basic idea of encapsulation and objects being owners of their methods. Consider your case: should runAction belong to RunActions or Animal? It belongs to both equally; better stated: it doesn't belong to either. This brings about a completely different programming model, one without encapsulation.

Upvotes: 3

BobTheBuilder
BobTheBuilder

Reputation: 19304

First of all, Dog and Cat should extend Animal:

public class Dog exttends Animal{  
    @Override
    public void doAction() {System.out.println("run");  
}

And use:

public class RunActions {  
    public void runAction(Animal a) {
        a.doAction();
    }
}

As both Dog and Cat are Animals, you can use Animal argument.

Upvotes: 1

Related Questions