As @YassinHajaj already said you can use the instanceof operator:
for (Animal animal : animals) {
if (animal instanceof Tiger) {
Tiger tiger = (Tiger) animal;
}
}
However this results in unstructured code, besides this is hard to maintain. I personally prefer an more object oriented way.
As @JBNizet suggested in a comment you can use polymorphism. That's good but not perfect: you will need to change the implementation of all animal classes in order to add some logic.
Visitor pattern
However if you combine this with the visitor pattern. This will become very powerful because you can separate the logic into a visitor class:
interface AnimalVisitor {
void visitLion(Lion lion);
void visitFish(Fish fish);
// Gets called when animal does not override the accept method:
void visitDefault(Animal animal);
}
class Animal {
public void accept(AnimalVisitor visitor) {
visitor.visitDefault(this);
}
}
class Fish extends Animal {
@Override
public void accept(AnimalVisitor visitor) {
visitor.visitFish(this);
}
}
class Lion extends Animal {
@Override
public void accept(AnimalVisitor visitor) {
visitor.visitLion(this);
}
}
Then you can easily replace your loop with something like this:
AnimalVisitor visitor = new AnimalVisitor() {
@Override
public void visitLion(Lion lion) {
// Do something with lion
}
@Override
public void visitFish(Fish fish) {
// Do something with fish
}
}
for (Animal animal : animals) {
animal.accept(visitor);
}
We can even take this one step further:
Reflective visitors
Since it is annoying to add for every different kind of animal an specific visitAnimal
method, we can make use of reflection to avoid this! This will result in smaller and cleaner code:
interface Visitable { }
class Animal implements Visitable {
// No accept method
}
class Fish extends Animal {}
class Goldfish extends Fish {}
class Shark extends Fish {}
// Take for example this visitor:
class BittenByVisitor extends ReflectiveVisitor {
private Surfer surfer;
public BitByVisitor(surfer) {
this.surfer = surfer;
}
// We only care about the sharks:
public void visit(Shark shark) {
surfer.die();
}
// Any other fish is just the same:
public void visit(Fish fish) {
surface.decreaseHealthBy(1);
}
// It also works with interfaces:
public void visit(VerySmallFish fish) {
// Do nothing by purpose!
}
}
And here an implementation of the reflective visitor:
abstract class ReflectiveVisitor {
public void visit(Visitable v) throws NoSuchMethodException {
Method m = findMethod(v);
try {
m.invoke(this, new Object[] { v });
}
catch ( IllegalAccessException e1 ) { /* code handling */ }
catch ( InvocationTargetException e2 ) { /* code handling */ }
}
private Method findMethod(Visitable v) throws NoSuchMethodException {
String methodName = "visit";
Class visitable = v.getClass();
while ( isAncestorOf("Visitable", visitable) {
Class visitor = getClass();
while ( isAncestorOf("Visitor", visitor) {
try {
Method m = visitor.getDeclaredMethod(methodName, new Class[]{visitable});
return m;
} catch ( NoSuchMethodException e ) {
visitor = visitor.getSuperclass();
}
}
visitable = visitable.getSuperclass();
}
String errMsg = "put error message here";
throw new NoSuchMethodException(errMsg);
}
private boolean isAncestorOf(String ancestorName, Class descendant) {
try {
return Class.forName(ancestorName).isAssignableFrom(descendant);
}
catch ( ClassNotFoundException e ) { /* code handling */ }
return false;
}
}
}
Source code is taken from this paper.
I personally like to use a reflective visitor which avoids the Visitable
class. You can define it like this:
class ReflectiveVisitor<T> {
public void visit(T visitable);
}