2012-06-27 2 views
4

Повторяя предыдущий пример, представьте себе зоопарк, в котором новоприбывшие животные должны быть «обработаны» Zookeeper (подумайте о том, чтобы проверить их в зоопарке). Процесс регистрации каждого животного зависит от его таксономии класса (млекопитающее, птица и т. Д.).Какое правильное использование генериков разрешает эту ошибку компиляции?

Процессы отличаются из-за фундаментальных различий между классами таксономии - например, у птиц есть крылья, у млекопитающих есть зубы. У вас также могут быть некоторые общие бит процесса, которые верны для всех животных, но я опустил их здесь.

Вот код:

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); 

    } 
} 

Там нет никаких предупреждений в любом месте, но ap.process (зверушка) не может скомпилировать. Я знаю, это потому, что AnimalProcessor<Bird>не типа AnimalProcessor<Animal>, но я не вижу, как решить проблему. Вызов <T extends Animal>getAnimalProcessor() вернет подходящий AnimalProcessor<T extends Animal>, но я не могу выразить это в коде.

Возможно, я не должен вытаскивать AnimalProcessor в первую очередь?

Цель состоит в том, чтобы, конечно, добавить Reptile, не меняя ядро.

+0

Почему животное интерфейс вместо абстрактный класс? Интерфейс должен быть своего рода совместным поведением. – carmenism

+0

Я принимаю ваше решение, и я изменил его в своем коде, но ошибка компиляции остается. – pjm56

ответ

5
public interface Animal<THIS extends Animal<THIS>> { 
    AnimalProcessor<THIS> getAnimalProcessor(); 
} 
public abstract class Mammal implements Animal<Mammal> { 

Немного похоже Enum<E extends Enum<E>>.

Возможно, лучше удалить метод.

+0

Я не верю, что видел этот подход раньше? У него есть имя? – pjm56

+0

Извините, удалите, какой метод? getAnimalProcessor()? Как бы вы могли сделать остальную часть функции кода в этом случае? – pjm56

+0

Следует отметить, что я намеренно добавил метод getAnimalProcessor, чтобы связать две параллельные иерархии наследования (Animal vs AnimalProcessor). – pjm56

2

AnimalProcessor.Java:

public interface AnimalProcessor<T extends Animal> { 
    public void process(Animal critter); 
} 

MammalProcessor.java

public class MammalProcessor implements AnimalProcessor<Mammal> { 
    @Override 
    public void process(Animal a) { 
     System.out.println("Tooth count is " + ((Mammal)a).getToothCount()); 
    } 
} 
+0

Спасибо Iga - я вижу, работая, но надеялась, что я смогу использовать дженерики умно, чтобы избежать понижения. Я думаю, что основная причина, по которой вы не можете уклониться от этого, заключается в том, что метод getAnimalProcessor позволяет вытащить процессор одного животного и потенциально использовать его с любым другим Animal (который может быть неправильным подтипом). Возможно, невозможно «скомпоновать этот круг», не применяя при этом наброски в местах, где вы будете определенными типами. Любой совет стиля в листингах li ke это? – pjm56

+0

Вы были поддержаны! – pjm56

Смежные вопросы