サブクラスでオーバーライドを有効にした場合、構造の正確さを保証するにはどうすればよいですか?
または、言い換えれば、これを保証する必要がありますか? (いいえの場合は、明確な理由を教えてください。)
問題を示す簡単な例を次に示します。
私たちの構造:偶数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); }
}
ただし、悪のボブは、この (間違った) サブクラスでそれをオーバーライドします。
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]
}
}
使用すると、ナンシーはいくつかの追加機能が必要になるため、ボブの (間違った) サブクラスを選択します。
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 が明示的に parent-class にキャストし直してもEvenArrayList
、問題は解決しません。
これまでのところ、これを回避するための解決策を考えています (アリスがライブラリを開発している間)。ただし、これは良い解決策ではありません。
(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); }
// ...
}
3 つのアプローチはほぼ同じです。
(1) は、同じコード ブロックを再利用できないため、あまり良くありません。
(2) と (3) は、メソッドの数がほぼ 2 倍になり、クラスが複雑になるため、あまり良くありません。さらに、オーバーライド可能なメソッドを誤って使用する可能性があるという「トラップ」が作成されます。
さらに、(2) と (3) では、関数呼び出しのオーバーヘッドが少し発生します。
この問題は両方の可能性があります
(1) セキュリティ上の問題:
ボブが意図的にいくつかのメソッドを誤ってオーバーライドし、他の人に彼の追加された (悪) 機能ライブラリを使用するように誘惑し、ナンシーがアリスのバージョンのメソッドを使用していると思っていても、キャスティングによってハッキングされた場合。
(2) ライブラリ開発者にとっての潜在的な罠:
Alice と Bob の両方の役割を果たしたとしても、サブクラスでの不適切なオーバーライドにより構造全体 (ArrayList でさえも) が壊れる可能性があり、エラーが発生している場所が原因から遠く離れているため、エラーの原因が簡単に見つからない可能性があります (決してご利用の際はお電話isEven()
ください)。
あなたの意見は何ですか?いくつかの解決策を提供してください。たくさんのご応募ありがとうございました!!