假设我有一个家长抽象动物训练师班:
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
方法只能用Trainer
和Trainables
的正确组合来调用。 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
,然后将对其进行优化。因此此检查不会影响性能。