假设我有一个家长抽象动物训练师班:

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


我希望对母动物教练员进行具体扩展,以仅对其定义的可训练对象完成训练。因此,如果我有如下具体的Dog Trainer:

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


使用DogTrainer的当前定义,我只能对trainingComplete类型的参数执行DogTrainer.Tricks。但我要强制要求,任何创建具体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>{
 ...
}


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

我怎样才能做到这一点?

最佳答案

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

在基类或接口内对this类型的强制约束介于棘手和不可能之间。一个常见的示例是Comparable接口,该接口不能以防止像class Foo implements Comparable<String>这样的实现的方式进行声明。

解决此问题的一种方法是使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,然后将对其进行优化。因此此检查不会影响性能。

07-26 09:19