2013-10-17 23 views
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); 
    } 

我希望父馴獸師的具體擴展來完成訓練只能由它定義的trainables 。所以,如果我有一個具體的馴狗師如下:

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

我想禁止這個,並允許擴展類只使用他們自己指定的Trainables。

我該怎麼做?

+0

我認爲這也解決了我的: [早期問題](http://stackoverflow.com/questions/19435006/force-children-to-use-enums-defined-within-the自己)。 – anishthecoder

回答

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,然後將優化掉哪些內容;所以這個檢查沒有性能影響。

+0

所以'Trainables'應該被定義爲'public interface Trainables '?我怎樣才能在父'教練' – anishthecoder

+1

執行,我擴大了答案 – Holger