![](/img/trans.png)
[英]How to ensure a certain methods gets called in abstract super-class from method in sub-class (Java)
[英]Ensure correctness in sub-class?
如果啟用子類中的覆蓋,如何確保結構的正確性?
或者,換句話說,我們必須確保這一點嗎? (如果否,請給出充分的理由。)
這是顯示問題的一個簡單示例:
我們的結構:一個僅包含偶數的ArrayList
。
庫開發人員Alice編寫了這個(正確的)類:
import java.util.ArrayList;
public class EvenArrayList {
private ArrayList<Integer> mArrayList = new ArrayList<Integer>();
public boolean isEven(int num) { return num % 2 == 0; }
public void addToList(int num) { if (isEven(num)) mArrayList.add(num); }
public void printList() { System.out.println(mArrayList); }
}
但是,邪惡的Bob用以下( 錯誤 )子類覆蓋了它:
public class EvilEvenArrayList extends EvenArrayList {
@Override
public boolean isEven(int num) { return true; }
}
使用后,Mandy選擇Alice的(正確)類:
public class Main {
public static void main(String[] args) {
EvenArrayList eal = new EvenArrayList();
eal.addToList(1);
eal.addToList(2);
eal.addToList(3);
eal.addToList(4);
eal.addToList(5);
eal.printList(); // prints [2, 4]
}
}
使用后,Nancy需要一些額外的功能,因此她選擇了Bob的( 錯誤的 )子類:
public class Main {
public static void main(String[] args) {
EvilEvenArrayList eeal = new EvilEvenArrayList();
eeal.addToList(1);
eeal.addToList(2);
eeal.addToList(3);
((EvenArrayList) eeal).addToList(4);
((EvenArrayList) eeal).addToList(5);
eeal.printList(); // prints [1, 2, 3, 4, 5]
}
}
如我們所見,即使是Nancy也明確地將其轉換回父類EvenArrayList
,也無濟於事。
到目前為止,我想到了避免這種情況的解決方案(而Alice開發了自己的庫)。 但是,這不是一個好的解決方案。
(1)不調用其他方法:
public class EvenArrayList {
// ...
public boolean isEven(int num) { return num % 2 == 0; }
public void addToList(int num) { if (num % 2 == 0) mArrayList.add(num); }
// ...
}
(2)或者,僅使用私有方法(因為它們不會被繼承):
public class EvenArrayList {
// ...
public boolean isEven(int num) { return _isEven(num); }
private boolean _isEven(int num) { return num % 2 == 0; }
public void addToList(int num) { if (_isEven(num)) mArrayList.add(num); }
// ...
}
(3)或者,僅使用最終方法(因為它們不能被覆蓋):
public class EvenArrayList {
// ...
public boolean isEven(int num) { return _isEven(num); }
public final boolean _isEven(int num) { return num % 2 == 0; }
public void addToList(int num) { if (_isEven(num)) mArrayList.add(num); }
// ...
}
三種方法大致相同。
(1)不太好,因為我們無法重用相同的代碼塊。
(2)和(3)不太好,因為它們通過使方法之間的幾乎兩倍加倍使類復雜化。 而且,它為我們自己創建了一個“陷阱”,我們可能會錯誤地使用可重寫的方法。
另外,(2)和(3)引入了一點函數調用開銷。
這個問題可能是
(1)安全問題:
如果Bob故意錯誤地重寫了某些方法,並誘使其他人使用他的添加的(邪惡的)功能庫,並且Nancy仍然被黑,即使她認為自己正在通過轉換使用Alice的方法版本。
(2)和圖書館開發人員的潛在陷阱:
即使我們同時扮演Alice和Bob的角色,子類中的錯誤覆蓋也可能破壞整個結構(甚至是ArrayList),並且錯誤的原因也可能不容易被發現,因為錯誤發生的位置與原因相去甚遠(我們永遠不會使用時調用isEven()
)。
你有什么意見? 請提供一些解決方案。 非常感謝您的投入!
首先,如果您不想讓您的類被子類化,或者您不希望對其進行設計以進行子類化,則可以(並且應該)使其最終化,或者至少記錄該類可擴展的事實。僅供內部使用,不應擴展。
現在,如果您的目標是對該類進行子類化,則確實必須對其進行設計,以使子類不會輕易破壞超類的不變式。 那是一項艱巨的任務。
在您的示例中,沒有理由允許重新定義isEven()
,因此您應該簡單地將其聲明為final(如果您想將其公開),或者將其私有(如果您不想將其公開)。 私有方法是隱式最終的。 其他方法相同。
也就是說,如果子類決定擴展您的類並破壞其不變式,那實際上不是您的問題。 這是子類和該子類的客戶的問題,他們選擇使用此損壞的子類。
但是要記住的是,如果您的基類是為擴展而設計的(本質上不是一件好事),那么您的業務就是確保在推出新版本的基類時這些擴展仍然有效。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.