サブクラスがパブリックにするかどうかを決定できるように、抽象クラスのどのアクセス修飾子をメソッドに使用する必要がありますか? Javaで修飾子を「オーバーライド」することは可能ですか?
public abstract class A {
??? void method();
}
public class B extends A {
@Override
public void method(){
// TODO
}
}
public class C extends B {
@Override
private void method(){
// TODO
}
}
誰かが次のように呼び出した場合、静的バインディングに問題があることを私は知っています。
// Will work
A foo = new B()
foo.method();
// Compiler ?
A foo = new C();
foo.method();
しかし、おそらく別の方法があります。どうすればそれを達成できますか?
制限を緩和することは可能ですが、より制限的にすることはできません。
public abstract class A {
protected void method();
}
public class B extends A {
@Override
public void method(){ // OK
}
}
public class C extends A {
@Override
private void method(){ // not allowed
}
}
元のメソッドをprivate
にすることも機能しません。そのようなメソッドはサブクラスに表示されないため、オーバーライドできないためです。
interface
sを使用して、メソッドを選択的に公開または非表示にすることをお勧めします。
public interface WithMethod {
// other methods
void method();
}
public interface WithoutMethod {
// other methods
// no 'method()'
}
public abstract class A {
protected void method();
}
public class B extends A implements WithMethod {
@Override
public void method(){
//TODO
}
}
public class C extends B implements WithoutMethod {
// no 'method()'
}
...その後、インターフェースを介してのみインスタンスを操作します。
メソッドをオーバーライドする場合、修飾子を変更できるのはより広い 1つだけであり、その逆はできません。たとえば、このコードは有効です。
_public abstract class A {
protected void method();
}
public class B extends A {
@Override
public void method() { }
}
_
ただし、可視性を絞り込もうとすると、コンパイル時エラーが発生します。
_public abstract class A {
protected void method();
}
public class B extends A {
@Override
private void method() {}
}
_
あなたの場合、C
がA
を実装しないようにすることをお勧めします。これは、A
の抽象化暗黙非プライベートのmethod()
:
_public class C {
private void method(){
//TODO
}
}
_
もう1つのオプションは、C
のmethod()
実装でRuntimeExceptionをスローすることです。
_public class C extends A {
@Override
public void method(){
throw new UnsupportedOperationException("C doesn't support callbacks to method()");
}
}
_
あなたが求めていることは、非常に正当な理由で不可能です。
リスコフの置換原則 は基本的に次のように述べています。クラスSは、別のクラスTのサブクラスであり、「Tオブジェクト」の出現を気付かずに「Sオブジェクト」に置き換えることができます。
Sがパブリックメソッドをプライベートに縮小することを許可する場合、それはもうできません。突然、T ...を使用しているときに呼び出すことができたメソッドは、Sで呼び出すことができなくなりました。
簡単に言えば、継承は単に空から落ちるものではありません。これは、プログラマーとしてのyouが担当するクラスのプロパティです。言い換えれば、継承とは、ソースコードに「クラスS拡張T」を書き留めるだけではありません。
多型のため、これは不可能です。次のことを考慮してください。クラスA
に、private
ではないアクセス修飾子を持つメソッドがあります。なぜプライベートではないのですか?それがプライベートである場合、他のクラスはその存在を知ることさえできなかったからです。したがって、それは他の何かである必要があり、他の何かはどこかからアクセス可能でなければなりません。
ここで、クラスC
のインスタンスをどこかに渡すと仮定しましょう。ただし、事前にA
にアップキャストしているため、このコードがどこかにあることになります:
_void somewhereMethod(A instance) {
instance.method(); // Ouch! Calling a private method on class C.
}
_
これがどのように壊れたかの良い例の1つは、Qtの QSaveFile です。 Javaとは異なり、C++では実際にはアクセス権限を下げることができます。そこで彼らは、close()
メソッドを禁止してまさにそれを行いました。彼らが最終的に得たのはQIODevice
サブクラスであり、実際にはもはやQIODevice
ではありません。 QSaveFile
へのポインタを_QIODevice*
_を受け入れるメソッドに渡しても、QIODevice
で公開されているため、close()
を呼び出すことができます。彼らはQSaveFile::close()
(プライベート)にabort()
を呼び出させることでこれを「修正」したので、そのようなことをすると、プログラムはすぐにクラッシュします。あまり良い「解決策」ではありませんが、これ以上の解決策はありません。そして、それは悪いOOデザインの単なる例です。それが、Javaがそれを許可しない理由です。
編集
あなたのクラスが抽象的であることを見逃したわけではありませんが、A
ではなく_B extends C
_という事実も見逃しました。このようにあなたがしたいことは完全に不可能です。メソッドがBでpublic
の場合、すべてのサブクラスでも公開されます。あなたができる唯一のことは、それが呼び出されるべきではないことを文書化することですそして多分それをオーバーライドしてUnsupportedOperationException
を投げます。しかし、それはQSaveFile
の場合と同じ問題につながります。クラスのユーザーは、それがC
のインスタンスであることさえ知らない可能性があるため、そのドキュメントを読む機会すらありません。
全体として、OOに関しては非常に悪い考えです。おそらく、この階層で解決しようとしている正確な問題について別の質問をする必要があります。また、それを適切に行う方法について、適切なアドバイスを得ることができます。
これが@Override
契約の一部です。
答えは:あなたが持っているものを達成する可能性はありません。
アクセスレベルは、オーバーライドされたメソッドのアクセスレベルよりも制限することはできません。例:スーパークラスメソッドがパブリックとして宣言されている場合、サブクラスのオーバーライドメソッドをプライベートにすることも保護することもできません。
これは、abstract
クラスのみに関する問題ではなく、すべてのクラスとメソッドに関する問題です。
理論:
決定された修飾子の順序があります。
public <- protected <- default-access X<- private
メソッドをオーバーライドすると、修飾子レベルを増やすことはできますが、減らすことはできません。例えば、
public -> []
protected -> [public]
default-access -> [public, default-access]
private -> []
練習:
あなたの場合、private
は最低修飾子であり、private
クラスのメンバーであるため、???
を何らかの修飾子に変えることはできません。継承されません。