It has been repeatedly said that the instanceof operator should not be used except in the equals() method, otherwise it's a bad OOP design.
Some wrote that this is a heavy operation, but it seems that, at least java, handles it pretty well (even more efficiently than Object.toString() comparison).
Can someone please explain, or direct me to some article which explains why is it a bad design?
Consider this:
Class Man{
doThingsWithAnimals(List<Animal> animals){
for(Animal animal : animals){
if(animal instanceOf Fish){
eatIt(animal);
}
else if(animal instanceof Dog){
playWithIt(animal);
}
}
}
...
}
The decision of what to do with the Animal, is up to the Man. Man's desires can also change occasionally, deciding to eat the Dog, and play with the Fish, while the Animals don't change.
If you think the instanceof operator is not the correct OOP design here, please tell how would you do it without the instanceof, and why?
The instanceof operator can be used to call a method based explicitly on the class of some object, instead of implicitly using an overridden method and polymorphism. Thus, inexperienced programmers may mistakenly use instanceof where an overidden method is more appropriate.
The instanceof operator in Java is used to check whether an object is an instance of a particular class or not. objectName instanceOf className; Here, if objectName is an instance of className , the operator returns true . Otherwise, it returns false .
instanceof is a binary operator we use to test if an object is of a given type. The result of the operation is either true or false. It's also known as a type comparison operator because it compares the instance with the type. Before casting an unknown object, the instanceof check should always be used.
Having a chain of "instanceof" operations is considered a "code smell". The standard answer is "use polymorphism".
instanceof
simply breaks the Open/Close principle. and/or Liskov substitution principle
If we are not enough abstract because of instanceof
usage, each time a new subclass makes an entrance, the main code gathering the logic of the application might be updated.
This is clearly not what we want, since it could potentially break the existing code and reduce its reusability.
Therefore, a good usage of polymorphism should be preferred over the basic use of conditional.
There's a good blog post called When Polymorphism Fails which is about this kind of scenario. Basically, you're right that it should be up to the Man
to decide what to do with each kind of Animal
. Otherwise, the code becomes fragmented and you end up violating principles such as Single Responsibility and Law of Demeter.
It wouldn't make sense to have code such as e.g. the following:
abstract class Animal {
abstract void interactWith(Man man);
}
class Fish extends Animal {
@Override
void interactWith(Man man) {
man.eat(this);
}
}
class Dog extends Animal {
@Override
void interactWith(Man man) {
man.playWith(this);
}
}
In that example, we're putting Man
's logic outside of the Man
class.
The problem with instanceof
is that if you have a large amount of Animal
s, you'll end up with a long if-else-if
for every one of them. It's hard to maintain and prone to errors where e.g. a new type of Animal
is added, but you forget to add it to the if-else-if
chain. (The visitor pattern is partly a solution to the latter problem, because when you add a new type to the visitor class, all of the implementations stop compiling and you're forced to go update them all.)
However, we can still use polymorphism to make the code simpler and avoid instanceof
.
For example, if we had a feeding routine such as:
if (animal instanceof Cat) {
animal.eat(catFood);
} else if (animal instanceof Dog) {
animal.eat(dogFood);
} else if (...) {
...
}
We could eliminate the if-else-if
by having methods such as Animal.eat(Food)
and Animal.getPreferredFood()
:
animal.eat(animal.getPreferredFood());
With methods such as Animal.isFood()
and Animal.isPet()
, the example in the question could be written without instanceof
as:
if (animal.isFood()) {
eatIt(animal);
} else if (animal.isPet()) {
playWithIt(animal);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With