How to get rid of instanceof check when using inheritance?

Question

Assume we have a class Animal, with subclasses as cat, eagle

Now I have a method:

public void process(Animal animal) {

   if (animal instanceof Cat) {
     if (!animal.meow()) {
        throw exception("cat does not meow");
     } else {
      animal.feedFish();
     }
   }

   if (animal instanceof eagle) {
      if (!animal.fly()) {
         throw exception("eagle does not fly");
      } else {
        animal.checkMaxFlightAltitude();
      }
   }
}

Here cat has 2 methods meow and feedfish which are completely different than eagle's methods fly and checkmaxflight

Most design patterns revolve around assumptions that subclasses have a common method like Shape draw() inherited by circle draw and square draw

  1. Is there some way to do validations on subclasses, such as cat and eagle without instanceof check ?

  2. Any good design pattern ( assuming subclasses dont share a method in base class ? )


Show source
| java   | inheritance   | oop   | design-patterns   2016-12-30 15:12 4 Answers

Answers to How to get rid of instanceof check when using inheritance? ( 4 )

  1. 2016-12-30 16:12

    You could have an abstract process method in Animal and implement it in the subclasses:

    class Animal {
      protected abstract void process();
      public static void process(Animal a) { a.process(); }
    }
    
    class Cat {
      void process() {
        if (!meow()) throw exception("cat does not meow");
        else feedFish();
      }
      public boolean meow() { ... }
      public void feedFish() { ... }
    }
    
  2. 2016-12-30 16:12

    This is when inheritance comes in handy.

    class Animal {
        abstract public void process(Animal animal);
    }
    class Cat extends Animal {
        @Override
        public void process(Animal animal) {
            if (!this.meow()) {
                throw exception("cat does not meow");
            } else {
                this.feedFish();
            }
        }
    }
    class Eagle extends Animal {
        @Override
        public void process(Animal animal) {
            if (!this.fly()) {
                throw exception("eagle does not fly");
            } else {
                this.checkMaxFlightAltitude();
            }
        }
    }
    
  3. 2016-12-30 16:12

    You could use double dispatch by employing a visitor.

    Example:

    public class Animal {
        public abstract void accept(AnimalVisitor v);
    
        public boolean meow() {return false;}
        public boolean fly() {return false;}
        public void feedFish() {};
        public void checkMaxFlightAltitude() {};
    
    }
    
    public class Cat extends Animal {
        public void accept(AnimalVisitor v) {
            v.visitCat(this);
        }
    
        public boolean meow() {return true;}
    }
    
    public class Eagle extends Animal {
        public void accept(AnimalVisitor v) {
            v.visitEagle(this);
        }
        public boolean fly() {return true;}
    }
    
    public interface AnimalVisitor {
        void visitEagle(Eagle eagle);
        void visitCat(Cat cat);
    }
    
    public class AnimalVisitorExample implements AnimalVisitor {
        public void visitEagle(Eagle eagle) {
            eagle.checkMaxFlightAltitude();
        }
    
        public void visitCat(Cat cat) {
            cat.feedFish();
        }
    }
    
    Animal animal = new Cat();
    animal.accept(new AnimalVisitorExample());
    
  4. 2016-12-30 16:12

    (1) Is there some way to do validations on subclasses, such as cat and eagle without instanceof check ?

    yes, there is. You could define a "validate" method (abstract in "Animal" class) and implement it in the specific subclasses. Depending on the validation result (e.g. exception / problem list) you can have the validate method throw some kind of "InvalidContentException" or provide the method call with an "ErrorHandler" that is informed about the bad things of an instance.

    (2) assuming that the subclasses don't share a method in the base class: well, that one is a bit counter intuitive. On one hand you want to be able to do something on an "Animal", yet you don't want to define that capability on it?

    You could define a Validator class that has separate validation methods (on for each kind of "Animal" subclass). That would eliminate the instanceof checks, however you would never be able to pass this Validator class other "Animals" (such as "Dog"), only "Cat" and "Eagle" (or subclasses thereof). You might also want to consider what you want to happen when passing subclasses of "Cat": are all sublasses of Cat validated in the same way or is there subclass specific behavior (like color, size, ...) for the different cat classes?

    --> I think you should ask yourself if you want to be able to validate animals in general. Without having insight into your problem domain (which might have reasons not to do it), I'd recommend to have a "validate" method on the animal. You could also go for a visitor pattern, but that requires the Animal to have a "accept(AnimalVisitor visitor)" method and is slightly more code to write (presumably more than you want to)

Leave a reply to - How to get rid of instanceof check when using inheritance?

◀ Go back