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

在每个培训师中,我都有一个实现此接口的 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”,它将一个枚举作为输入并将其保存到一个集合中。所以

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”方法,而是在父“培训师”类中定义一些可以强制执行枚举类型的东西......所以这是枚举和泛型的奇怪组合。

这可能吗?

4

4 回答 4

28

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

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

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

在这种情况下,您想要的泛型 Kung Fu 会复杂上一级,因为 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;
    }
}

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

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> {}
于 2013-10-11T03:54:26.377 回答
1

即使已经接受了答案,我也会将其发布以供进一步考虑

如果您阅读此问题,请考虑使用更松散耦合的元素、聚合等,而不是高度具体和复杂的类型层次结构和泛型。

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

可能更好的是更多的集合,聚合等以确保类型安全和弹性(不同的训练师对不同的动物进行任何组合,包括混合,而不是锁定一个人成为一些超特定能力的超特定训练大师)特定的动物,仅此而已)。

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

于 2013-10-11T20:18:10.817 回答
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 );
}
于 2013-10-11T12:14:55.723 回答
-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) 抽象化,您需要在每个 Trainer 实现分类中实现。

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);
     }
 }
于 2013-10-11T03:53:41.153 回答