【问题标题】:Force children to use enums defined within themselves强制孩子使用自己定义的枚举
【发布时间】:2013-10-17 19:01:40
【问题描述】:

假设我有一个父级抽象动物训练师课程:

public abstract class Trainer 
  <A extends Animal, 
   E extends Enum<E> & Trainables>{
    protected EnumSet<E> completed;
    public void trainingComplete(E trainable){
      trainingComplete.add(trainable);
    }

我想要父级动物训练师的具体扩展,以仅完成由它定义的可训练对象的训练。所以如果我有一个具体的训狗师如下:

public class DogTrainer extends Trainer<Dog, DogTrainer.Tricks>{
  public enum Tricks implements Trainables {
    FETCH, GROWL, SIT, HEEL;
  }
}

对于DogTrainer 的当前定义,我只能对DogTrainer.Tricks 类型的参数执行trainingComplete。但我想强制任何创建具体Trainer 的人都应该允许trainingComplete() 用于它在自身内部定义的Trainables

换句话说,我当前设计的问题是,如果我有另一个教练,如下所示:

public class PoliceDogTrainer extends Trainer<Dog, PoliceDogTrainer.Tricks>{
  public enum Tricks implements Trainables {
     FIND_DRUGS, FIND_BOMB, FIND_BODY;
  }
}

没有什么能阻止某人定义另一个试图教狗的胭脂训练师,警察把戏:

public class RougeTrainer extends Trainer<Dog, PoliceDogTrainer.Tricks>{
 ...
}

我想禁止这样做,并允许扩展类仅使用他们自己指定的可训练对象。

我该怎么做?

【问题讨论】:

标签: java generics enums enumset


【解决方案1】:

您可以将enums 设为非public,但这不能由抽象基类强制执行。另一种方法是通过添加必须与Trainer 类匹配的类型参数来使Trainables 泛型。这不会强制 enum 成为内部类(这是不可能的),但对于符合要求的子类,则不能创建 RogueTrainer

在基类或接口中对this 的类型实施约束介于棘手和不可能之间。一个众所周知的例子是Comparable 接口,它不能被声明为阻止像class Foo implements Comparable&lt;String&gt; 这样的实现。

绕过这个问题的一种方法是让Trainer引用一个参数,例如

public interface Trainables<T extends Trainer<?,? extends Trainables<T>>>
…
public abstract class Trainer 
  <A extends Animal,
   E extends Enum<E> & Trainables<? extends Trainer<A,E>>> {

  protected EnumSet<E> completed;

  void trainingCompleteImpl(E trainable) {
    completed.add(trainable);
  }

  public static <A extends Animal, T extends Trainer<A,E>,
    E extends Enum<E> & Trainables<T>> void trainingComplete(T t, E trainable) {
    t.trainingCompleteImpl(trainable);
  }
}

public class PoliceDogTrainer
  extends Trainer<Dog, PoliceDogTrainer.Tricks> {

  public enum Tricks implements Trainables<PoliceDogTrainer> {
     FIND_DRUGS, FIND_BOMB, FIND_BODY;
  }
}

public static 方法只能通过TrainerTrainables 的正确组合调用。 trainingCompleteImpl 方法可以被同一包中的受信任子类调用和覆盖。如果你不想这样,你可以内联方法的代码并完全删除实例方法。

_

另一种方法是为Trainer 创建一个类型参数,并在运行时强制该参数与this 匹配:

public interface Trainables<T extends Trainer<?,T,? extends Trainables<T>>>
…
public abstract class Trainer 
  <A extends Animal, T extends Trainer<A,T,E>,
   E extends Enum<E> & Trainables<T>> {

  protected EnumSet<E> completed;

  /** sub-classes should implements this as {@code return this}*/
  protected abstract T selfReference();

  void trainingComplete(E trainable) {
    if(selfReference()!=this) throw new IllegalStateException();
    completed.add(trainable);
  }
}
public class PoliceDogTrainer
  extends Trainer<Dog, PoliceDogTrainer, PoliceDogTrainer.Tricks> {

  public enum Tricks implements Trainables<PoliceDogTrainer> {
     FIND_DRUGS, FIND_BOMB, FIND_BODY;
  }

  @Override
  protected final PoliceDogTrainer selfReference()
  {
    return this;
  }
}

因此,对于不合格的Trainer 实现,selfReference() 不能实现为return this;,这很容易被检测到。对于符合要求的实现,JVM 将内联 selfReference 方法并查看 this==this 然后将被优化掉;所以这个检查对性能没有影响。

【讨论】:

  • 所以Trainables应该定义为public interface Trainables&lt;T extends Trainer&gt;?我如何在父 Trainer 中强制执行该操作
  • 我扩展了答案
猜你喜欢
  • 2011-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-15
  • 1970-01-01
相关资源
最近更新 更多