【问题标题】:"Sets" of a particular enum type but with generics特定枚举类型但具有泛型的“集合”
【发布时间】:2013-10-11 03:29:47
【问题描述】:

假设我有一个抽象类

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

这可能吗?

【问题讨论】:

  • 在每个声明中,Enum 应该是 enum
  • 啊是的 =) 谢谢!更新
  • 显示动物的定义
  • @Bohemian,你能告诉你需要用 def 看到什么吗?动物?我想对于这个类比,它只是一些常见的动物属性(平均体重、属、种),也许还有一些常见的方法......你打算用动物定义去哪里......?
  • 对于I'd like something in the parent 'Animal' class that can be Enum-type enforced,我想你的意思是I'd like something in the parent ***'Trainer'*** class that can be Enum-type enforced,对吧?

标签: java generics enums


【解决方案1】:

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

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

但我会更进一步,在它们适用的特定 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> {}

【讨论】:

  • +1 心中有一个复杂的答案,但这太棒了!
  • 这里的要点是将泛型参数绑定为两种类型的方式。您可以在可以控制的任何地方应用此技术。
  • 我感觉Trainer里面少了一个Animal的类型参数。在 OP 的原始问题中,Trainer 可以引用动物的类型,但在您建议的方法中,我们不能(虽然在 OP 的示例代码中,它没有显示用法......)
  • 所以我认为它可能看起来像Trainer&lt;A, T extends TrainingAction&lt;A&gt; &amp; Enum&lt;T&gt; &gt;
  • 当然我的意思是"John than can teach horse to trot and a dog to bark",而不是"John that can teach horse or dog to trot or bark"。因此,我会针对可以独立添加到教师的特定动物的一些训练能力,甚至是一组更通用的能力(几乎我们可以教每一种动物奔跑)。但这当然不是设计问题。
【解决方案2】:

尽管进行了设计分析,但我认为最适合您的情况(最接近您已经创建的设计)的答案是:

  • 您的DogTrainer.TrainablesTrainingActions&lt;Dog&gt;
  • 您的HorseTrainer.TrainablesTrainingActions&lt;Horse&gt;

一般来说,您的Trainables 已经是TrainingActions&lt;T extends Animal&gt;

所以你可以只提供Set&lt;TrainingActions&lt;T&gt;&gt; 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 );
}

【讨论】:

  • 我在过渡期间正好使用了这个策略 =) 但不幸的是,它并不能阻止某人将 PoliceDogTrainer 的可训练对象添加到 PuppyTrainer 中......
  • @anishthecoder 就像我在 cmets 在接受的答案中提到的那样 - 我认为问题不在于你所说的,但总体上不是最佳设计 - 指定 PoliceDogTrainer 等就像进入最大继承OOP 中的用法 - 您因类型受限而迷失了方向。我认为应该有更简单的聚合等,而不是复杂的类型层次结构
【解决方案3】:

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

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

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

可能更好的是更多的集合、聚合等,以确保 将安全性和弹性结合在一起(不同的教练有不同的 任何组合的不同动物的能力,包括混合, 对于某些人来说,不锁定一个人是一个超级特定的培训大师 特定动物的超特定能力仅此而已)。

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

【讨论】:

  • 你能举个例子吗?我有兴趣将您的解决方案与接受的答案进行比较
  • 查看我的其他回复。我没有确切或类似的实现。我只是一般地提倡“组合优于继承”。因为DogTrainer 不能也是HorseTrainer?因此,例如,您可以拥有 TrainerPersonList&lt;TrainingSkill&gt; nad,如果该人能够训练某个实体,则 它具有技能收集方面的技能。然后,DogTrainer 类的逻辑可以移动到DogTrainingSkill
【解决方案4】:

是的,您可以将您的 TrainablesSet&lt;Trainables&gt; 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);
     }
 }

【讨论】:

  • 请耐心等待。驯马师通常不会教马吠……他们也许可以,但那是一匹奇怪的马。通过问题,可训练对象需要分开。因此,这不是解决方案。
猜你喜欢
  • 1970-01-01
  • 2018-09-30
  • 1970-01-01
  • 1970-01-01
  • 2016-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多