Repeating the previous example, imagine a zoo where newly arrived animals should be treated by Zookeeper (think about checking them out at the zoo). Each animal registration process depends on its class taxonomy (mammals, birds, etc.).
The processes differ due to the fundamental differences between the classes of taxonomy - for example, birds have wings, mammals have teeth. You may also have some common bits of the process that are true for all animals, but I 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(); }
Bird.java
public abstract class Bird implements Animal { @Override public AnimalProcessor<Bird> getAnimalProcessor() { return new BirdProcessor(); }
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();
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, but ap.process (crter) cannot compile. I know this because AnimalProcessor<Bird> not of type AnimalProcessor<Animal> , but I donβt see how to solve the problem. Calling <T extends Animal> getAnimalProcessor() will return the appropriate AnimalProcessor<T extends Animal> , but I cannot express it in the code.
Perhaps I should not pull out AnimalProcessor in the first place?
The goal is, of course, to add a Reptile without changing the kernel.