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