2013-10-11 64 views
34
“设置”

比方说,我有一个抽象类特定枚举类型,但与仿制药

public abstract class Trainer<T extends Animal>{} 

我有这样具体的培训师:

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

每个“教员”的有一组他们可以训练动物做的固定技巧,我想用Enums来做。所以我有一个接口:

public interface TrainingActions<T extends Animal>{} 

和在每个培训师,我有一个枚举,实现此接口。所以:

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

现在在每个训练课,我想一个方法说“trainingComplete”,是以枚举的一个作为输入,并将其保存到一组。所以

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”方法的设置,我想在父东西“教练”类,可以被Enum类型强制执行......因此这是Enums和泛型的奇怪组合。

这可能吗?

+1

在每个声明,'枚举'应该是'enum'。 –

+0

ahh yes =)thanx!更新 – anishthecoder

+0

显示Animal的定义 – Bohemian

回答

28

要指定绑定到一个接口,它是一个枚举,你需要一个通用的路口,它看起来像:

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

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

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

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> {} 
+0

+1有一个复杂的答案,但这只是真棒! –

+0

如果我无法控制def,该怎么办?动物....? – anishthecoder

+0

这里的要点是你将泛型参数绑定为两种类型的方式。只要有控制权,您都可以应用此技术。 – Bohemian

-2

是的,你可以将你的Trainables,Set<Trainables> completedtrainingComplete(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)的抽象,你需要在每个训练implementaion clasees实施。

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); 
    } 
} 
+2

要有耐心。驯马师通常不会教马吠叫......他们可能能够,但这是一匹古怪的马。由这个问题,可训练者需要分开。因此这不是一个解决方案。 – Charlie

1

尽管设计分析,我认为最 - 你的情况(最接近你已经创建的设计)的答案是:

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

一般来说,你的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); 

如果您确定要执行一个枚举为你的接口:

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

我正在使用这个策略在过渡=)但它不能阻止有人添加PoliceDogTrainer的Trainable到PuppyTrainer .. – anishthecoder

+0

@anishthecoder就像我在接受的答案中的评论中提到的 - 我认为问题不会在于你说什么,但总体而言不是最好的设计 - 指定'PoliceDogTrainer'等就像进入OOP中的最大继承用法 - 你太过于苛刻的类型而丢失了。我认为有更多的简单的聚合等,而不是复杂的类型层次 –

1

我将张贴进一步审议,甚至还有已经接受的答案

如果你读到这个问题,考虑使用更加松散的耦合元素,用聚合等代替高度特殊的非常复杂的类型层次结构和泛型。

只需简单的聚合,您就可以实现至少相同的逻辑安全类型,但松耦合设计。就像我在评论中提到:

Propably更好的将是多个集合,agregations等,保证 类型安全和elasticyty在一起(不同的培训师不同 能力不同动物的任意组合,包括混合, 没有锁定一个人成为一个超具体的训练大师 特定动物的超具体能力和没有更多)。

当然我指的是“约翰不是可以教马小跑和狗 树皮”,而不是“约翰,可以教马或者狗小跑或树皮”

+0

你能给一个工作的例子吗?我有兴趣将您的解决方案与已接受的答案进行比较 –

+0

请参阅我的其他回复。我没有确切的或类似的实现。我只是普遍宣传“构图而非继承”。因为'DogTrainer'不能成为HorseTrainer吗?所以,举个例子,如果一个人能够训练某个实体,那么他可以拥有一个'Trainer'或'Person',并且拥有'List 'nad。然后,来自'DogTrainer'类的逻辑可以转移到'DogTrainingSkill' –