简体   繁体   中英

How does one use polymorphism instead of instanceof? (And why?)

If we take the code below:

Shape p1 = new Square();
Square c1;
if(p1 instanceof Square) {
  c1 = (Square) p1;
}

What does it mean to prefer polymorphism to instanceof , and incidentally, why is it better?

Edit: I understand what polymorphism is; what I'm missing is how one would use it rather than instanceof .

The main difference between if...else... (or switch, or Visitor), and between polymorphism is modularity. There's so called open-closed principle, which basically means, that when you add a new feature to an existing program, the less changes you make in existing code the better (because every change requires some work, and may introduce bugs). So let's compare the amount of changes:

  • adding a new method (eg. you have paint(), and getArea(), let's add getCircumference()): with if-else solution you only have to alter just one file - the file which will contain the new method. With polymorphism, you have to alter all your implementations of Shape class.

  • adding a new kind of Shape (you have Square, Circle - let's add Triangle): with if-else solution you have to review all existing classes with if-else and add a new if branch for Triangle; with polymporphism all you have is to add a new class and implement all required methods in it.

So if...else... or polymorphism: it depends on modularity. If you expect that many new sublasses will be added later, use polymorphism; if you expect that many new methods will be added later, use if...else..., and in the class put only the most "basic" methods like accessors. Or in other words: when you expect to have many if...else... branches, you should rather use polymorphism, when you expect few such branches, just stay with if...else...

Additionally: when you expect few if...else... branches, but in lots of places, you should consider encapsulating this if...else... with Visitor pattern or just making an enum with a separate case for each branch.

The idea is that you shouldn't have to care what kind of shape you are dealing with. For example, if Shape defines an abstract draw() method, then Triangles, Squares and anything else that extends Shape will also have the same method.

A simple definition for polymorphism is "treating different types as if they are the same", ie using the same interface.

In an ideal world, we don't want to have to worry about what specific type of object we are dealing with, only the interface of a more general type that covers all usage scenarios in its interface.

Shape p1 = new Square();
Shape p2 = new Triangle();
p1.draw();
p2.draw();

In this code, we are directly calling Shape.draw() on p1 and p2. We don't care what the implementation class does, only what is defined by the interface (or abstract parent class).

Edit: Regarding the example in the question, It is generally recommended to avoid that kind of code pattern by encapsulating behaviour where possible. Using instanceof can be considered a code smell, as you would have to update all your conditionals whenever you add a new class.

Consider the following

abstract class Shape {
   public abstract int getEdgesNumber();
}

class Square extends Shape {
    public int getEdgesNumber(){
        return 4;
    }
}

class Circle extends Shape {
    public int getEdgesNumber(){
        return 1; //not so sure it counts as one but for the example is fine ^^'
    }
}     

Shape square = new Square();
int squareEdgesNumber = square.getEdgesNumber();

Shape circle = new Circle();
int circleEdgesNumber = circle.getEdgesNumber();

A Square and a Circle both implement the getEdgesNumber() method, you just invoke it and get the result, based on the particular implementation.

You do not need to know if you're dealing with a Square or with a Circle , you just invoke the method you need and rely on the underlying implementation of the object.

Also, have a look at how the docs are explaining it .

That's not really a strong example, but here's what your code would look like.

Square c1 = new Square();
Shape p1 = c1;

(given that Square extends Shape of course)

Much better isn't it?

As for "why is it better", the other answers give out some important points.

Polymorphism lets you change the behaviour of something depending on which type is it. Not sure how to explain it with your example since you could just assign it to a Square straight away if it is important that it is a Square for some reason. Sometimes you need to subclass as it might have additional behaviour etc, but consider this example:

class Shape
{
    abstract void draw();
}

class Square extends Shape
{
    void draw()
    {
        // square drawing goes here
    }
}

The draw method here is an example of polymorphism as we have a base class Shape that says that all shapes now how to draw themselves, but only the Square knows how to draw a Square.

I assume "Shape" is an interface, and "Square" an implementation of that interface.

Now, if you need to call a method that's declared for the Shape interface (typical example is Shape.getArea()), you shouldn't care whether it's a Square or something else, and call that function.

Some people believe that there is a time and a place for instanceof , and that the Visitor pattern isn't always a complete and appropriate replacement for it. Some other people hiss and scowl. Rinse, repeat.

I think your example could be improved by trying to do something meaningful (such as drawing, or counting sides, etc), because the OOP philosophy would fundamentally avoid the situation you illustrate in your example. For example, an OOP design would either declare c1 as a Shape rather than a Square , or simply use the p1 variable.

As an aside, if you're after the situation where c1 is null if it's not a Square, or set to p1 if it is, a similar "as" operator exists, which I'm a fan of.

Shape p1  = (Math.random()>0.5) ? new Square() : new Circle();
Square c1 = p1 as Square;
// c1 is null or not, depending on p1's type.

It's no more OO than instanceof in my view, but then again I don't really consider instanceof to be inately "non-OO".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM