Reusing a previous example, imagine a zoo where newly arriving animals have to be “processed” by the Zookeeper (think of checking them in to the zoo). Each animal’s check-in process depends on its class taxonomy (Mammal, Bird, etc).
The processes differ because of the fundamental differences between the taxonomy classes – for example, Birds have wings, Mammals have teeth. You might also have some shared bits of the process which are true for all animals, but I’ve omitted them here.
Here is the code:
Animal.java
public interface Animal {
public AnimalProcessor<? extends Animal> getAnimalProcessor();
}
Mammal.java
public abstract class Mammal implements Animal {
@Override
public AnimalProcessor<Mammal> getAnimalProcessor() {
return new MammalProcessor();
}
// Specific to mammals
public abstract int getToothCount();
}
Bird.java
public abstract class Bird implements Animal {
@Override
public AnimalProcessor<Bird> getAnimalProcessor() {
return new BirdProcessor();
}
// Specific to birds
public abstract float getWingspan();
}
AnimalProcessor.java
public interface AnimalProcessor<T extends Animal> {
public void process(T critter);
}
MammalProcessor.java
public class MammalProcessor implements AnimalProcessor<Mammal> {
@Override
public void process(Mammal a) {
System.out.println("Tooth count is " + a.getToothCount());
}
}
BirdProcessor.java
public class BirdProcessor implements AnimalProcessor<Bird> {
@Override
public void process(Bird a) {
System.out.print("Wingspan is " + a.getWingspan());
}
}
Badger.java
public class Badger extends Mammal {
@Override
public int getToothCount() {
return 40;
}
}
Condor.java
public class Condor extends Bird {
@Override
public float getWingspan() {
return 2.9f;
}
}
ZooKeeper.java
import java.util.List;
public class ZooKeeper {
public void processNewAnimals(List<Animal> newcomers)
{
for(Animal critter : newcomers)
{
AnimalProcessor<? extends Animal> ap = critter.getAnimalProcessor();
// This line has a compilation error!
ap.process(critter);
}
}
}
MainClass.java
import java.util.LinkedList;
import java.util.List;
public class MainClass {
public static void main(String[] args) {
ZooKeeper keeper = new ZooKeeper();
List<Animal> animals = new LinkedList<Animal>();
animals.add(new Badger());
animals.add(new Condor());
keeper.processNewAnimals(animals);
}
}
There are no warnings anywhere, but ap.process(critter) cannot compile.
I know this is because AnimalProcessor<Bird> is not of type AnimalProcessor<Animal> but I cannot see how resolve the issue. The call to <T extends Animal> getAnimalProcessor() will return a suitable AnimalProcessor<T extends Animal>, but I cannot express this in the code.
Perhaps I should not be pulling the AnimalProcessor out in the first place?
The goal is, of course, to be able to add Reptile, without changing the core.
A little like
Enum<E extends Enum<E>>.It may be a better idea to remove the method.