Java: Is polymorphism applicable only to methods with identical signatures?

The only examples of overriding polymorphic methods I've ever seen include methods that take no parameters or at least have the same parameter lists. Consider a general example of Animal / Dog / Cat:

public abstract class Animal { public abstract void makeSound(); } public class Dog extends Animal { public void makeSound() { System.out.println("woof"); } } public class Cat extends Animal { public void makeSound() { System.out.println("meow"); } } public class ListenToAnimals { public static void main(String[] args) { AnimalFactory factory = new AnimalFactory(); Animal a = factory.getRandomAnimal(); // generate a dog or cat at random a.makeSound(); } } 

In this case, everything works fine. Now let's add another method that is partially implemented in an abstract class when obtaining more specific behavior in subclasses:

 public abstract class Animal { public abstract void makeSound(); public void speak(String name) { System.out.println("My name is " + name); } } public class Dog extends Animal { public void makeSound() { System.out.println("woof"); } public void speak(String name) { super.speak(name); System.out.println("I'm a dog"); } } public class Cat extends Animal { public void makeSound() { System.out.println("meow"); } public void speak(String name, int lives) { super.speak(name); System.out.println("I'm a cat and I have " + lives + " lives"); } } public class ListenToAnimals { public static void main(String[] args) { AnimalFactory factory = new AnimalFactory(); Animal a = factory.getRandomAnimal(); // generate a dog or cat at random a.makeSound(); // a.speak(NOW WHAT? } } 

In this last (commented) line of the main method, I don’t know what to put there, because I don’t know what type of Animal I have. I did not have to worry about this before because makeSound () did not accept any arguments. But they say (), and the arguments depend on the type of Animal.

I read that some languages, such as Objective-C, allow variable argument lists, so this question never arises. Does anyone know of a good way to implement this kind of thing in Java?

+4
source share
9 answers

You are confusing method overrides and method overloads. In your example, the Cat class has two methods:

 public void speak(String name) // It gets this from its super class public void speak(String name, int lives) 

Overloading is a way of defining methods with similar functions, but with different parameters. There would be no difference if you named the method this way:

 public void speakWithLives(String name, int lives) 

To avoid confusion, the recommendation in java is to use the @Override annotation when you try to override a method. Therefore:

  // Compiles @Override public void speak(String name) // Doesn't compile - no overriding occurs! @Override public void speak(String name, int lives) 

EDIT: Other answers mention this, but I repeat it for emphasis. Adding a new method has led to the fact that the Cat class can no longer be represented as Animal in all cases, thereby eliminating the advantage of polymorphism. To use the new method, you need to reduce it to the Cat type:

 Animal mightBeACat = ... if(mightBeACat instanceof Cat) { Cat definitelyACat = (Cat) mightBeACat; definitelyACat.speak("Whiskers", 9); } else { // Definitely not a cat! mightBeACat.speak("Fred"); } 

The code checker in my IDE puts a warning in instanceof because the keyword indicates a possible rejection of polymorphic abstraction.

+6
source

Your Cat example is no longer polymorphic, as you must know this Cat to pass this parameter. Even if Java allowed it, how would you use it?

+5
source

As far as I know, Java does not allow you to do this. talking (name, life) is now just a Cat function. Some languages ​​allow this flexibility. To force java to allow this, you can make paramater an array of objects or some other collection.

However, keep in mind that when you speak English, you should know which parameters should pass independently, so the point is somewhat controversial.

+2
source

When you call the polymorphic method as follows:

 a.speak("Gerorge"); 

You do not need to know what type of Animal was created because it is the goal of polymorphism. Also, since you have a user suggestion:

 super.speak(name); 

Both Cat Dogs will have Animal behavior plus their own behavior.

+1
source

You can do

 public void speak(Map ... mappedData) { System.out.println("My name is " + mappedData.get("name")+ " and I have "+mappedData.get("lives"); } 

However, I would advise making the Cat instance variable life and passing your factory a default value (or the constructor has a default parameter for it).

0
source
 In this case best way is to use a DTO, public class SpeakDTO { //use getters and setters when you actually implement this String name; int lives; } public class Dog extends Animal { public void speak(SpeakDTO dto) { super.speak(dto.name); System.out.println("I'm a dog"); } } public class Cat extends Animal { public void speak(SpeakDTO dto) { super.speak(dto.name); System.out.println("I'm a cat and I have " + dto.lives + " lives"); } } public class ListenToAnimals { public static void main(String[] args) { AnimalFactory factory = new AnimalFactory(); Animal a = factory.getRandomAnimal(); // generate a dog or cat at random a.makeSound(); SpeakDTO dto = new SpeakDTO(); dto.name = "big cat"; dto.lives = 7; a.speak(dto); } } 
0
source

If you want to make such a call, you can use reflection to get the class:

 if (a.getclass() == Cat.class) { // speak like a cat } else if (a.getclass() == Dog.class) { . . . 

Of course, this may not be the best design, and meditation should be used with care.

0
source

Java also has lists of variable arguments, but I would say that this is not the best way to do this, at least not under any circumstances.

If subclasses have behavior that is not defined by the interface, you do not have many options in Java that are not verbose or a bit uncomfortable.

You might have a word () that takes a marker interface and delegates the arg construct to a factory. You can pass a parameter map. You can use varargs.

Ultimately, you need to know what to pass to the method, no matter what language.

0
source

I agree with the comments about how you really violated polymorphism if you need to know the type of object before you can call the talk method. If you absolutely MUST have access to both methods of conversation, here is one way to implement it.

 public class Animal { public void speak(String name) { throw new UnsupportedOperationException("Speak without lives not implemented"); } public void speak(String name, int lives) { throw new UnsupportedOperationException("Speak with lives not implemented"); } } public class Dog extends Animal { public void speak(String name) { System.out.println("My name is " + name); System.out.println("I'm a dog"); } } public class Cat extends Animal { public void speak(String name, int lives) { System.out.println("My name is " + name); System.out.println("I'm a cat and I have " + lives + " lives"); } } 

Alternatively, you can put UnsupportedOperationExceptions in child classes (or you can use a checked exception). I don’t advocate any of these at all , but I think this is the closest way to implement what you requested, and I really saw systems that used something like this.

0
source

All Articles