Doubts about the use of polymorphism, as well as how polymorphism is associated with casting?

I give lessons in the basics of the Java programming language for students studying this subject in college.

Today one of them made me embarrass her question, so I told her to give me only one day to think about the problem, and I will give her the most accurate answer as soon as I can.

She told me that the teacher was very angry when she used the instanceof keyword in her exam.

In addition, she said that the teacher said that there is no way to prove how polymorphism worked if she used the word.

I thought a lot to try to find a way to prove that in some cases we need to use instanceof , and also that even if we use it, there is still some polymorphism in this approach.

So, here is an example I made:

 public interface Animal { public void talk(); } class Dog implements Animal { public void talk() { System.out.println("Woof!"); } } public class Cat implements Animal { public void talk() { System.out.println("Meow!"); } public void climbToATree() { System.out.println("Hop, the cat just cimbed to the tree"); } } class Hippopotamus implements Animal { public void talk() { System.out.println("Roar!"); } } public class Main { public static void main(String[] args) { //APPROACH 1 makeItTalk(new Cat()); makeItTalk(new Dog()); makeItTalk(new Hippopotamus()); //APPROACH 2 makeItClimbToATree(new Cat()); makeItClimbToATree(new Hippopotamus()); } public static void makeItTalk(Animal animal) { animal.talk(); } public static void makeItClimbToATree(Animal animal) { if(animal instanceof Cat) { ((Cat)animal).climbToATree(); } else { System.err.println("That animal cannot climb to a tree"); } } } 

My findings are as follows:

  • The first approach (APPROACH 1) is a simple demonstration of how to program an interface, not an implementation. I think that polymorphism is clearly visible in the parameters of the makeItTalk(Animal animal) method, as well as in the name of the method call using the animal object. (This part is fine)

  • The second part bothers me. She used instanceof at some point in her exam (I don’t know what their exam looked like), and it was mistaken because the teacher said that you did not prove polymorphism.

To help her understand when she can use instanceof , I thought about telling her that she can use it when the method she needs to call is not in the interface, but it is in only one of the implementation classes.

As you can see, only cats can climb trees, and it would be illogical to get a hippo or dog to climb a tree. I think this might be an example of when instanceof should be used.

  • But what about polymorphism in approach 2?

  • How many applications of polymorphism do you see there (only approach 2)?

  • Do you think that there is some kind of polymorphism in this line?

    ((Cat)animal).climbToATree();

I think this is so, because in order to achieve Casting of this type, objects must have an IS-A relationship, in some ways this is polymorphism.

  • Do you think this is right?

  • If so, how would you explain in your own words that casting is dependent on polymorphism?

+6
source share
8 answers

In the above example, there is no need to call

 makeItClimbToATree (new Hippopotamus ()); 

This could have been easily avoided if makeItClimbToATree would not have expected an animal, but something more specific that could actually climb a tree. The need to allow animals and therefore use the specimen is not visible. If you manage animals in the list of animals, this will be more obvious.

While the ircmaxells explanation begins perfectly, introducing Koala and other TreeClimbers, he does not see the second extension that is hidden in the sea anemone: various features of animals like seaAnemoneHider, winterSleeping, blueEyed, bugEating, etc. etc. You will end up with a boolean over a boolean, constantly recompiling the base class, and breaking the expanding client classes, which will require recompilation and will not be able to present your own capabilities in a similar way.

Customer A will need Customer B to declare a NotBugEatingException so that your behavior falls into the base class.

Getting to know your own interfaces, combined with instanceof, is a cleaner approach and more flexible. Client A can determine how dumpLikeAPenguin and trumpeter B of the client, without knowing about each other, both do not affect the Animal class and do not provoke useless recompilation.

 import java.util.*; interface Animal { public void talk (); } interface TreeClimbing { public void climbToATree (); } class Dog implements Animal { public void talk () { System.out.println("Woof!"); } } class Cat implements Animal, TreeClimbing { public void talk () { System.out.println("Meow!"); } public void climbToATree () { System.out.println ("on top!"); } } public class TreeCriterion { public static void main(String[] args) { List <Animal> animals = new ArrayList <Animal> (); animals.add (new Cat ()); animals.add (new Dog ()); discuss (animals); upTheTree (animals); } public static void discuss (List <Animal> animals) { for (Animal a : animals) a.talk (); } public static void upTheTree (List <Animal> animals) { for (Animal a : animals) { if (a instanceof TreeClimbing) ((TreeClimbing) a).climbToATree (); } } } 

We do not need a third animal, dog or cat. I made them visible by default, not public, so that the entire example fits into a single file.

+4
source

The reason why the instanceof method is considered bad is simple. Cats are not the only Animal that could climb a tree.

What happens if you need to add the Koala class along the way. Then your simple if will not be so simple or . Then what happens when you add another class? and one more. And one more. This is the main reason instanceof is considered bad. Because it connects the implementation to a specific class, rather than opening it to the called person to determine what to do.

Just implement the makeItClimbToATree() method to throw a CantClimbTreesException if an animal is raised that cannot rise. So you have the best of both worlds. Easy to implement and easy to expand.

IMHO, instanceof has only 1 really valid use: in the test case, for checking the returned instance from the method, it matches the expected return type (in safe languages ​​other than the type).

In principle, any other use can be more than likely reorganized or developed differently to deny the need for its use.

Another way to look at this: Polymorphism eliminates almost all conditional statements from your code. The only conditions that you cannot get rid of (at least all of them) are methods for creating objects (for example, in a factory, where he must choose a class based on the run-time argument). Almost any other conditional can be replaced by polymorphism. Therefore, everything that conditional execution does is by definition anti-polymorphic. This does not mean that it is bad (there is a huge difference between “Good and Good”), but in an academic discussion it is not polymorphic ...

Never forget the 60/60 rule. 60% of your total development time will be spent on maintaining the code you wrote, and 60% of this time will be spent on adding new features. Make maintenance easier and your life easier. This is why instanceof bad. This facilitates the initial design, but complicates the long-term maintenance (which is in any case more expensive) ...

+5
source

Do you think that there is some kind of polymorphism in this line?

 ((Cat)animal).climbToATree(); 

Not. Moreover, Cat is a leaf class in the example.

I think this is so, because in order to achieve Casting of this type, objects must have an IS-A relationship, in some ways this is polymorphism.

Polymorphism requires an IS-A relationship, but not vice versa.

Polymorphism is when you send (potentially) different methods based on an abstract interface. If you do not have this dispatch, then it does not use polymorphism. In your example, using instanceof to pass to a class without subclasses, you remove the need to submit.

(Of course, there are several ways to “make polymorphism” in Java. It can be implemented using interfaces, using abstract classes or using specific classes with subclasses ... or hypothetical subclasses that can be written to Future Interfaces (and dispatching based on an interface) are usually the best way, as they provide a clean separation of the API from the identity of the class.)

And in a separate note, the use of instanceof , as it is usually a sign of poor design and / or poor modeling. In particular, this is firmly connected with the assumption that only cats can rise, which is trivially falsified if we include other animals in the model / program. If this happens, your code will break.

+2
source

Perhaps I do not have enough points, and I do not understand the context of the exam question, but whether Animal overcome the tree, be part of the class that implements Animal . For example, if Animal is an interface, you may have a boolean isCapableOfClimbing() method, and then each implementation class will be able to indicate its capabilities.

Then the method that the animal tried to make could use this. This does not make sense for a method that tries to make an animal climb a tree, check if it is an instance of a certain class, since then this method indicates what should be indicated in the implementation class. A simple method should not provide behavior for the class that it uses.

Regarding the question of when to use instanceof , the place where it will almost always be used is to redefine the equals() method of the class, since it accepts only Object , and you usually need to ensure it has the same type , so it can be distinguished, and then meaningfully compared.

0
source

What about the code? It solves the problem of community by sharing tree-climbing as another interface that you can implement or not on your animals. This is better suited to solve the problem: tree climbing is not an inalienable property of all animals, only from a subset of them. At least for me it looks a lot clearer and more elegant than throwing NotImplementedException s.

 public interface Animal { public void talk(); } public interface AnimalCanClimbTrees extends Animal { public void climbToATree(); } public class Dog implements Animal { public void talk() { System.out.println("Woof!"); } } /* Animal is probably not needed, but being explicit is never bad */ public class Cat implements Animal, AnimalCanClimbTrees { public void talk() { System.out.println("Meow!"); } public void climbToATree() { System.out.println("Hop, the cat just cimbed to the tree"); } } class Hippopotamus implements Animal { public void talk() { System.out.println("Roar!"); } } public class Main { public static void main(String[] args) { //APPROACH 1 makeItTalk(new Cat()); makeItTalk(new Dog()); makeItTalk(new Hippopotamus()); //APPROACH 2 makeItClimbToATree(new Cat()); makeItClimbToATree(new Hippopotamus()); } public static void makeItTalk(Animal animal) { animal.talk(); } public static void makeItClimbToATree(Animal animal) { if(animal instanceof AnimalCanClimbTrees) { ((AnimalCanClimbTrees)animal).climbToATree(); } else { System.err.println("That animal cannot climb to a tree"); } } } 
0
source

The instanceof operator has nothing to do with polymorphism. It is simply used to check if an object is an instance of a particular class. You see that this operator is used a lot in the equals() method, because the method takes a generic Object as a parameter:

 public class Cat implements Animal{ @Override public boolean equals(Object obj){ if (obj == null || !obj instanceof Cat){ //obj is null or not a "Cat", so can't be equal return false; } if (this == obj){ //it the same instance so it must be equal return true; } Cat catObj = (Cat)obj; //cast to "Cat" return this.getName().equals(catObj.getName()); //compare the two objects } } 

If the class does not implement the method, it should throw an exception. I believe the "official" exception you should throw is an UnsupportedOperationException . To be "correct," I think the Animal interface should have a public void climbToATree(); method public void climbToATree(); . The climbToATree() methods in the Dog and Hippo classes must UnsupportedOperationException because they cannot implement this method. But if you throw this exception very often, maybe something is wrong with your object model, as this is not an ordinary thing that I don't think about.

Also note that it is useful (but not necessary) to use the @Override annotation with polymorphic Java programming. This will cause a compilation error if a method with this annotation does not override the parent method, implements an abstract method, or (in Java 6) implements an interface method. This can help catch any errors you make in the method signature. For instance:

 public String tostring(){ return "foobar"; } 

Without annotation, the program will be compiled and successfully launched. But that was not your intention! You wanted to override toString (), but you accidentally spelled the name incorrectly!

0
source

I am surprised that no one wrote anything about Late Binding. Polymorphism in Java = late binding. The called method will be attached to the object when we finally know its type. In your example:

  if(animal instanceof Cat) { ((Cat)animal).climbToATree(); } 

You call climbToATree() on the Cat object, so the compiler accepts it. At run time, there is no need to check the type of the caller, since climbToATree() belongs only to Cat . And so there is no polymorphism in these lines of code.

About casting related to polymorphism, this is not so. Casting simply limits the fields that are shared in both objects if the cast is legal. You can do it:

 class A { int getInt() {} } class B extends A { int getInt() {} } // in main A a = new B(); A b = (A)a; b.getInt(); // This would still call class B getInt(); 

The listing itself did not add a value, getInt () was bound at runtime to the runtime type a , which was class B.

0
source

The polymorphic and OOP approach would be to place the makeItClimbToATree method in the Animal interface:

 public interface Animal{ public void talk(); public void makeItClimbToATree(); } 

Then Animal developers would provide a method behavior that, for everyone else but Cat, could throw an exception. This is polymorphic because you work with different implementations of Animal with one method.

A function that uses the instanceOf operator is considered to be a “bad” OOP because it requires knowledge of all implementation types to determine the behavior of a method.

-2
source

Source: https://habr.com/ru/post/889744/


All Articles