Avoiding big if statements and instanceof

Animal

public abstract class Animal { String name; public Animal(String name) { this.name = name; } } 

a lion

 public class Lion extends Animal { public Lion(String name) { super(name); // TODO Auto-generated constructor stub } public void roar() { System.out.println("Roar"); } } 

Deer

 public class Deer extends Animal { public Deer(String name) { super(name); } public void runAway() { System.out.println("Running..."); } } 

TestAnimals

 public class TestAnimals { public static void main(String[] args) { Animal lion = new Lion("Geo"); Animal deer1 = new Deer("D1"); Animal deer2 = new Deer("D2"); List<Animal> li = new ArrayList<Animal>(); li.add(lion); li.add(deer1); li.add(deer2); for (Animal a : li) { if (a instanceof Lion) { Lion l = (Lion) a; l.roar(); } if (a instanceof Deer) { Deer l = (Deer) a; l.runAway(); } } } } 

Is there a better way to iterate through a list without using? In the above example, this looks fine, but if you have many extensions of the base class, then we need so many if.Is blocks there is a design template or a principle for solving this problem?

+16
java casting iteration
Oct. 14 '10 at 6:59
source share
9 answers

An elegant way to avoid instanceof without inventing any new artificial method in the base class (with a non descriptive name like performAction or doWhatYouAreSupposedToDo ) should use a visitor pattern . Here is an example:

Animal

 import java.util.*; abstract class Animal { String name; public Animal(String name) { this.name = name; } public abstract void accept(AnimalVisitor av); // <-- Open up for visitors. } 

Lion and deer

 class Lion extends Animal { public Lion(String name) { super(name); } public void roar() { System.out.println("Roar"); } public void accept(AnimalVisitor av) { av.visit(this); // <-- Accept and call visit. } } class Deer extends Animal { public Deer(String name) { super(name); } public void runAway() { System.out.println("Running..."); } public void accept(AnimalVisitor av) { av.visit(this); // <-- Accept and call visit. } } 

Visitor

 interface AnimalVisitor { void visit(Lion l); void visit(Deer d); } class ActionVisitor implements AnimalVisitor { public void visit(Deer d) { d.runAway(); } public void visit(Lion l) { l.roar(); } } 

TestAnimals

 public class TestAnimals { public static void main(String[] args) { Animal lion = new Lion("Geo"); Animal deer1 = new Deer("D1"); Animal deer2 = new Deer("D2"); List<Animal> li = new ArrayList<Animal>(); li.add(lion); li.add(deer1); li.add(deer2); for (Animal a : li) a.accept(new ActionVisitor()); // <-- Accept / visit. } } 
+29
Oct 14 '10 at 7:09
source share

Animal

 public abstract class Animal { String name; public Animal(String name) { this.name = name; } public abstract void exhibitNaturalBehaviour(); } 

a lion

 public class Lion extends Animal { public Lion(String name) { super(name); } public void exhibitNaturalBehaviour() { System.out.println("Roar"); } } 

Deer

 public class Deer extends Animal { public Deer(String name) { super(name); } public void exhibitNaturalBehaviour() { System.out.println("Running..."); } } 

Testanimals

 public class TestAnimals { public static void main(String[] args) { Animal[] animalArr = {new Lion("Geo"), new Deer("D1"), new Deer("D2")}; for (Animal a : animalArr) { a.exhibitNaturalBehaviour(); } } } 
+12
Oct. 14 '10 at 7:06
source share

Yes, give a method called action() in an abstract class, implement it in both child classes, one will roar, the other will run

+5
Oct 14 '10 at 7:02
source share

It turns out that instanceof is faster than the visitor pattern presented above; I think this should question us if the visitor pattern is more elegant than instanceof when it does the same slower with more lines of code?

Here is my test. I compared 3 methods: the visitor pattern above, instanceof, and the explicit type field in Animal.

OS: Windows 7 Enterprise SP1, 64-bit
Processor: Intel (R) Core (TM) i7 CPU 860 @ 2.80 GHz 2.93 GHz
RAM: 8.00 GB
JRE: 1.7.0_21-b11, 32-bit

 import java.util.ArrayList; import java.util.List; public class AnimalTest1 { public static void main(String[] args) { Animal lion = new Lion("Geo"); Animal deer1 = new Deer("D1"); Animal deer2 = new Deer("D2"); List<Animal> li = new ArrayList<Animal>(); li.add(lion); li.add(deer1); li.add(deer2); int reps = 10000000; long start, elapsed; start = System.nanoTime(); for (int i = 0; i < reps; i++) { for (Animal a : li) a.accept(new ActionVisitor()); // <-- Accept / visit. } elapsed = System.nanoTime() - start; System.out.println("Visitor took " + elapsed + " ns"); start = System.nanoTime(); for (int i = 0; i < reps; i++) { for (Animal a : li) { if (a instanceof Lion) { ((Lion) a).roar(); } else if (a instanceof Deer) { ((Deer) a).runAway(); } } } elapsed = System.nanoTime() - start; System.out.println("instanceof took " + elapsed + " ns"); start = System.nanoTime(); for (int i = 0; i < reps; i++) { for (Animal a : li) { switch (a.type) { case Animal.LION_TYPE: ((Lion) a).roar(); break; case Animal.DEER_TYPE: ((Deer) a).runAway(); break; } } } elapsed = System.nanoTime() - start; System.out.println("type constant took " + elapsed + " ns"); } } abstract class Animal { public static final int LION_TYPE = 0; public static final int DEER_TYPE = 1; String name; public final int type; public Animal(String name, int type) { this.name = name; this.type = type; } public abstract void accept(AnimalVisitor av); // <-- Open up for visitors. } class Lion extends Animal { public Lion(String name) { super(name, LION_TYPE); } public void roar() { // System.out.println("Roar"); } public void accept(AnimalVisitor av) { av.visit(this); // <-- Accept and call visit. } } class Deer extends Animal { public Deer(String name) { super(name, DEER_TYPE); } public void runAway() { // System.out.println("Running..."); } public void accept(AnimalVisitor av) { av.visit(this); // <-- Accept and call visit. } } interface AnimalVisitor { void visit(Lion l); void visit(Deer d); } class ActionVisitor implements AnimalVisitor { public void visit(Deer d) { d.runAway(); } public void visit(Lion l) { l.roar(); } } 

Test results:

The visitor accepted 920842192 ns
instanceof took 511837398 ns
type of constant took 535296640 ns

This visitor pattern introduces 2 additional method calls that are not needed with instanceof. This is probably why it is slower.

Not that performance is the only consideration, but pay attention to how 2 instances are faster than even a switch statement with two cases. A lot of people were worried about instance performance, but that should fix the concern.

As a Java developer, I feel frustrated when people have a dogmatic attitude about avoiding using instanceof, because there were several times in my work, I wanted to clear or write new clean code using instanceof, but the staff / superiors did not approve this approach because they more or less blindly accepted the idea that instanceof should never be used. I feel upset because this moment often leads home with examples of toys that do not reflect the real problems of the business.

Whenever you do modular software development, there will always be times when type-specific decisions need to be isolated from their respective types so that types have as few dependencies as possible.

This visitor pattern does not violate modularity, but is not an excellent alternative to instanceof.

+3
May 7 '13 at 16:08
source share

If your method is not polymorphic, you cannot do without translation. To make it polymorphic, declare the method in the base class and override it in the descendant classes.

+2
Oct. 14 '10 at 7:03
source share

Here you have a List animals. Usually, when you have a list of objects, all these objects should be able to do the same without being cast.

So, the best two solutions:

  • Having a common method for two specific classes (the so-called abstract in Animal )
  • Separate Lion from Deer from the start and have two different lists.
+2
Oct. 14 '10 at 7:10
source share

Support for matching patterns in the language eliminates the need for an ugly visitor pattern.

See this Scala code for example:

 abstract class Animal(name: String) class Lion(name: String) extends Animal(name) { def roar() { println("Roar!") } } class Deer(name: String) extends Animal(name) { def runAway() { println("Running!") } } object TestAnimals { def main(args: Array[String]) { val animals = List(new Lion("Geo"), new Deer("D1"), new Deer("D2")) for(animal <- animals) animal match { case l: Lion => l.roar() case d: Deer => d.runAway() case _ => () } } } 
+2
Oct 14 '10 at 7:39
source share

Consider adding an interface for an action (Roar, Run away, etc.), which is installed on the animal in the constructor. Then use an abstract method such as act () in the Animal class, which is called similar to what Adeel has.

This will allow you to swap actions to act across the field at any time.

+1
Oct 14 '10 at 7:17
source share

The simplest approach is for the superclass to follow the default behavior.

 public enum AnimalBehaviour { Deer { public void runAway() { System.out.println("Running..."); } }, Lion { public void roar() { System.out.println("Roar"); } } public void runAway() { } public void roar() { } } public class Animal { private final String name; private final AnimalBehaviour behaviour; public Animal(String name, AnimalBehaviour behaviour) { this.name = name; this.behaviour = behaviour; } public void runAway() { behaviour.runAway(); } public void roar() { behaviour.roar(); } } public class TestAnimals { public static void main(String... args) { Animal[] animals = { new Animal("Geo", AnimalBehaviour.Lion), new Animal("Bambi", AnimalBehaviour.Deer), new Animal("D2", AnimalBehaviour.Deer) }; for (Animal a : animals) { a.roar(); a.runAway(); } } } 
+1
Oct. 15 '10 at 6:09
source share



All Articles