有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java强制子级使用自己定义的枚举

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

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

我想禁止这样做,并允许扩展类只使用他们自己指定的可培训内容

我该怎么做


共 (1) 个答案

  1. # 1 楼答案

    可以使enum的非public但抽象基类不能强制执行。另一种方法是通过添加必须与Trainer类匹配的类型参数,使Trainables成为泛型。这不会强制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,然后对其进行优化;因此,此检查对性能没有影响