Animal
public abstract class Animal {
String name;
public Animal(String name) {
this.name = name;
}
}
Lion<
An elegant way of avoiding instanceof
without inventing some new artificial method in the base class (with a non-descriptive name such as performAction
or doWhatYouAreSupposedToDo
) is to use the 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.
}
}
It turns out that instanceof is faster than the visitor pattern presented above; I think this should make us question, is the visitor pattern really more elegant than instanceof when it's doing the same thing more slowly with more lines of code?
Here's my test. I compared 3 methods: the visitor pattern above, instanceof, and an 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:
Visitor took 920842192 ns
instanceof took 511837398 ns
type constant took 535296640 ns
This visitor pattern introduces 2 extra method calls that are unnecessary with instanceof. This is probably why it's slower.
Not that performance is the only consideration, but notice how 2 instanceofs are faster than even a 2-case switch statement. Plenty of people have worried about the performance of instanceof, but this should put the worry to rest.
As a Java Developer, I feel frustrated when people have a dogmatic attitude about avoiding the use of instanceof, because there have been several times in my work I wanted to clean up or write new clean code by using instanceof, but coworkers/superiors didn't approve of this approach , because they have more or less blindly accepted the idea that instanceof should never be used. I feel frustrated because this point is often driven home with toy examples that don't reflect real business concerns.
Whenever you pursue modular software design, there will always be times when type-dependent decisions need to be isolated from the types in question, so that the types have as few dependencies as possible.
This visitor pattern doesn't break modularity, but it's not a superior alternative to instanceof.
Animal
public abstract class Animal {
String name;
public Animal(String name) {
this.name = name;
}
public abstract void exhibitNaturalBehaviour();
}
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();
}
}
}