1

親抽象動物調教師クラスがあるとしましょう:

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のみ実行できます。しかし、具象を作成する人は誰でも、具象がそれ自体で定義することを許可する必要があることを強制したいと思います。trainingCompleteDogTrainer.TricksTrainertrainingComplete()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>{
 ...
}

私はこれを禁止し、クラスを拡張して、指定したTrainablesのみを使用できるようにしたいと考えています。

どうやってやるの?

4

1 に答える 1

1

enums を非にすることはできpublicますが、抽象基本クラスによって強制することはできません。別の方法は、クラスTrainablesに一致する必要がある型パラメーターを追加してジェネリックにすることです。Trainerこれは を内部クラスにすることを強制するものではありませんenum(それは不可能です) が、準拠するサブクラスの場合、 noRogueTrainerを作成することはできません。

this基底クラスまたはインターフェイス内の型に制約を適用することは、トリッキーと不可能の間のどこかにあります。よく知られている例の 1 つは、Comparableのような実装を防ぐために宣言できないインターフェイスclass Foo implements Comparable<String>です。

この問題を回避する 1 つの方法は、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の適切な組み合わせでのみ呼び出すことができます。このメソッドは、同じパッケージ内の信頼できるサブクラスによって呼び出しおよびオーバーライドできます。これが望ましくない場合は、メソッドのコードをインライン化し、インスタンス メソッドを完全に削除できます。TrainerTrainablestrainingCompleteImpl

_

別の方法として、 の型パラメーターを作成し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化して、最適化されるものを確認します。したがって、このチェックによるパフォーマンスへの影響はありません。

于 2013-10-17T19:36:06.130 に答える