“设置”一个特定的枚举types,但generics

假设我有一个抽象类

public abstract class Trainer<T extends Animal>{} 

我有特定的培训师,如:

 public DogTrainer extends Trainer<Dog>{} public HorseTrainer extends Trainer<Horse>{} 

这些“训练者”中的每一个都有一套固定的技巧,他们可以训练动物去做,我想用Enums来做。 所以我有一个接口:

 public interface TrainingActions<T extends Animal>{} 

在每个培训师,我有一个Enum实现这个接口。 所以:

 public DogTrainer extends Trainer<Dog>{ public enum Trainables implements TrainingActions<Dog>{ BARK, BITE, ROLLOVER, FETCH; } } public HorseTrainer extends Trainer<Horse>{ public enum Trainables implements TrainingActions<Horse>{ JUMP, TROT, REARUP; } } 

现在在每个Trainer类中,我想要一个方法说'trainingComplete',将其中一个Enums作为input,并将其保存到一个集合中。 所以

 public DogTrainer extends Trainer<Dog>{ public enum Trainables implements TrainingActions<Dog>{ BARK, BITE, ROLLOVER, FETCH; } public Set<Trainables> completed = new HashSet<Trainables>(); public void trainingComplete(Trainables t){completed.add(t);} } 

然而,我不希望在每个培训者中定义“完成”集合,而是在每个培训者中定义“trainingComplete”方法,我希望父类“培训师”类中的某些东西可以通过Enumtypes强制执行。因此,这是一个枚举和generics的奇怪组合。

这可能吗?

为了给一个接口指定一个绑定,并且它是一个枚举,你需要一个通用的交集 ,如下所示:

 class MyClass<T extends Enum<T> & SomeInterface> {} 

请注意,当类和接口相交时,类必须出现在接口之前。

在这种情况下,你想要的generics功夫是一个更复杂的层次,因为TrainingAction枚举的接口必须自己引用动物types。

 class Trainer<A extends Animal, T extends Enum<T> & TrainingActions<A>> {} 

一个完整的工作示例,基于您的发布代码,编译是:

 public class Animal {} public interface TrainingActions<T extends Animal> {} /** * A trainer that can teach an animal a suitable set of tricks * @param <A> The type of Animal * @param <T> The enum of TrainingActions that can be taught to the specified Animal */ public abstract class Trainer<A extends Animal, T extends Enum<T> & TrainingActions<A>> { private Set<T> completed = new HashSet<T>(); public void trainingComplete(T t) { completed.add(t); } } public class Dog extends Animal {}; public class DogTrainer extends Trainer<Dog, DogTrainer.Trainables> { public enum Trainables implements TrainingActions<Dog> { BARK, BITE, ROLLOVER, FETCH; } } 

但是我会更进一步,在它们适用的特定Animal的类中定义几个TrainingActions枚举:

 public class Dog extends Animal { public enum BasicTrainables implements TrainingActions<Dog> { SIT, COME, STAY, HEEL; } public enum IntermediateTrainables implements TrainingActions<Dog> { BARK, BITE, ROLLOVER, FETCH; } public enum AdvacedTrainables implements TrainingActions<Dog> { SNIFF_DRUGS, FIND_PERSON, ATTACK, GUARD; } }; public class PuppyTrainer extends Trainer<Dog, Dog.BasicTrainables> {} public class ObedienceTrainer extends Trainer<Dog, Dog.IntermediateTrainables> {} public class PoliceTrainer extends Trainer<Dog, Dog.AdvacedTrainables> {} 

尽pipedevise分析,我认为最接近你的情况(最接近你已经创build的devise)的答案是:

  • 你的DogTrainer.TrainablesTrainingActions<Dog>
  • 你的HorseTrainer.TrainablesTrainingActions<Horse>

一般来说,你的TrainingActions<T extends Animal> Trainables已经是一个TrainingActions<T extends Animal>

所以你可以只提供Set<TrainingActions<T>> completed ,因为你已经有了你的动物信息在T

 abstract class Trainer<T extends Animal> { Set<TrainingActions<T>> completed = new HashSet<TrainingActions<T>>(); public void add(TrainingActions<T> t ) { completed.add( t ); } } 

而你正是你想要的。

用法:

 DogTrainer tr = new DogTrainer(); tr.add( DogTrainer.Trainables.BITE ); 

如果您确定要为您的界面强制使用Enum,请执行以下操作:

 public <E extends Enum<?> & TrainingActions<T>> void add( E t ) { completed.add( t ); } 

我会张贴进一步的考虑,即使已经接受的答案

如果你阅读这个问题,考虑使用更加松散的耦合元素,聚合等,而不是高度特定和复杂的types层次结构和generics。

只需要简单的聚合就可以实现至less相同的types和逻辑安全性,但是松耦合devise。 就像我在评论中提到的:

为了确保types的安全性和弹性(比如不同的动物具有不同的能力,包括混合,不locking一个人成为超级特定能力的超级特定训练大师)只有特定的动物,没有更多 )。

当然,我的意思是一个“约翰不能教马,小狗叫”,而不是“能教马或者狗跑步或者吠叫的约翰”

是的,你可以移动你的TrainablesSet<Trainables> completedtrainingComplete(Trainables t) Set<Trainables> completed trainingComplete(Trainables t)到课程训练师。

这样你就不需要写每个DogTrainer,HorseTrainer等等这些代码了…

  abstract class Trainer<T extends Animal>{ public enum Trainables implements TrainingActions<Animal>{ BARK, BITE, ROLLOVER, FETCH; } public Set<Trainables> completed = new HashSet<Trainables>(); public void trainingComplete(Trainables t){completed.add(t);} } 

否则,使trainingComplete(Trainables t)抽象,你需要在每个培训师执行实施分类。

 abstract class Trainer<T extends Animal>{ public enum Trainables implements TrainingActions<Animal>{ BARK, BITE, ROLLOVER, FETCH; } public Set<Trainables> completed = new HashSet<Trainables>(); abstract public void trainingComplete(Trainables t); } 

现在你的DogTrainer和HorseTrainer正在实施trainingComplete(Trainables t)

  public class DogTrainer extends Trainer<Dog>{ public void trainingComplete(Trainables t){ completed.add(t); } } public class HorseTrainer extends Trainer<Horse>{ public void trainingComplete(Trainables t){ completed.add(t); } }