web-dev-qa-db-ja.com

オーバーライドするメソッドは、基本クラスのものとは異なるアクセス指定子を持つことができますか?

サブクラスがパブリックにするかどうかを決定できるように、抽象クラスのどのアクセス修飾子をメソッドに使用する必要がありますか? 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();

しかし、おそらく別の方法があります。どうすればそれを達成できますか?

21
jam

制限を緩和することは可能ですが、より制限的にすることはできません。

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にすることも機能しません。そのようなメソッドはサブクラスに表示されないため、オーバーライドできないためです。

interfacesを使用して、メソッドを選択的に公開または非表示にすることをお勧めします。

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()'
}

...その後、インターフェースを介してのみインスタンスを操作します。

12
Jiri Tousek

メソッドをオーバーライドする場合、修飾子を変更できるのはより広い 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() {}
}
_

あなたの場合、CAを実装しないようにすることをお勧めします。これは、Aの抽象化暗黙非プライベートのmethod()

_public class C {
    private void method(){
      //TODO
    }
}
_

もう1つのオプションは、Cmethod()実装でRuntimeExceptionをスローすることです。

_public class C extends A {

    @Override
    public void method(){
        throw new UnsupportedOperationException("C doesn't support callbacks to method()");
    }
}
_
8

あなたが求めていることは、非常に正当な理由で不可能です。

リスコフの置換原則 は基本的に次のように述べています。クラス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に関しては非常に悪い考えです。おそらく、この階層で解決しようとしている正確な問題について別の質問をする必要があります。また、それを適切に行う方法について、適切なアドバイスを得ることができます。

4
Sergei Tachenov

これが@Override契約の一部です。

答えは:あなたが持っているものを達成する可能性はありません。

アクセスレベルは、オーバーライドされたメソッドのアクセスレベルよりも制限することはできません。例:スーパークラスメソッドがパブリックとして宣言されている場合、サブクラスのオーバーライドメソッドをプライベートにすることも保護することもできません。

これは、abstractクラスのみに関する問題ではなく、すべてのクラスとメソッドに関する問題です。

3
Yassin Hajaj

理論:

決定された修飾子の順序があります。

public <- protected <- default-access X<- private

メソッドをオーバーライドすると、修飾子レベルを増やすことはできますが、減らすことはできません。例えば、

public -> []
protected -> [public]
default-access -> [public, default-access]
private -> []

練習:

あなたの場合、private最低修飾子であり、privateクラスのメンバーであるため、???を何らかの修飾子に変えることはできません。継承されません。

3
Andrew Tobilko