Beware of instanceof operator

Many hold that the instanceof operator should be used only as a last resort, and that an overridden method is usually (but not always) a better alternative.

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.

A common exception to this guideline, however, is the use of instanceof within an equals method.

From Effective C++, by Scott Meyers :

"Anytime you find yourself writing code of the form "if the object is of type T1, then do something, but if it's of type T2, then do something else," slap yourself.

Here is an example of the type of abuse Scott Meyers is speaking of:


/**
* Naive, incorrect use of instanceof.
*/
public final class BadInstanceOf {

  public static void doSomething(Animal aAnimal){
    if (aAnimal instanceof Fish){
      Fish fish = (Fish)aAnimal;
      fish.swim();
    }
    else if (aAnimal instanceof Spider){
      Spider spider = (Spider)aAnimal;
      spider.crawl();
    }
  }

  // PRIVATE
  private static class Animal {}

  private static final class Fish extends Animal {
    void swim(){}
  }
  private static final class Spider extends Animal {
    void crawl(){}
  }
} 

The mistake is corrected by using an overridable method:

/**
* Using polymorphism to call different methods.
* Does not use instanceof.
*/
public final class BadInstanceOfFixed {

  public static void main(String... aArgs){
    log("Starting...");
     Animal animal = new Animal();
     doSomething(animal);
     
     //repoint the same 'animal' reference to other objects:
     
     animal = new Fish();
     doSomething(animal);
     
     animal = new Spider();
     doSomething(animal);
     
     log("Done.");
  }

  /**
  * This method implementation doesn't care at all 
  * about Fish/Spider. It just knows that it has 
  * been passed an Animal. Different versions of 
  * 'move' are called, specific to each Animal. 
  */
  public static void doSomething(Animal aAnimal){
    aAnimal.move();
  }

  // PRIVATE 
  private static class Animal {
    void move(){
      log("Move like an animal...");
    }
  }

  private static final class Fish extends Animal {
    @Override void move(){
      log("Move like a fish...");
    }
  }
  private static final class Spider extends Animal {
    @Override void move(){
      log("Move like a spider...");
    }
  }

  private static void log(String aMessage){
    System.out.println(aMessage);
  }
} 

An example run of this class:
Starting...
Move like an animal...
Move like a fish...
Move like a spider...
Done.


See Also :
Implementing equals
Avoid basic style errors
Would you use this technique?
Yes   No   Undecided   
© 2014 Hirondelle Systems | Source Code | Contact | License | RSS
Individual code snippets can be used under this BSD license - Last updated on September 21, 2013.
Over 2,000,000 unique IPs last year - Built with WEB4J.
- In Memoriam : Bill Dirani -