14

デフォルトでアクセス指定子が public であるメソッドを持つクラスがあります。ここで、このクラスをサブクラスに拡張し、このメソッドをオーバーライドしてアクセス指定子を「プライベート」にしたいと考えています。このコードをコンパイルすると、コンパイル エラーが発生します。

「より弱いアクセス権限を割り当てようとしています」。

サブクラスに弱い権限を割り当てることの何が問題なのか、誰か説明してもらえませんか?

コンパイル エラーの原因となったコードは次のとおりです。

class Superclass 
{
    void foo() 
    {
        System.out.println("Superclass.foo");
    }
}

class Subclass extends Superclass 
{
    private void foo() 
    {
        System.out.println("Subclass.foo");
    }
}
4

8 に答える 8

32

簡単に言えば、型の代入性が損なわれるため、許可されていません。リスコフ置換原理 (LSP)も参照してください。

要点は、Java (および他のプログラミング言語) におけるポリモーフィズムは、サブクラスのインスタンスをスーパークラスのインスタンスであるかのように扱えることに依存しているということです。しかし、メソッドがサブクラスで制限されている場合、アクセス規則でメソッドの呼び出しが許可されているかどうかをコンパイラが判断できないことがわかります...

たとえば、サンプルコードが合法であると仮定しましょう:

// Assume this code is in some other class ...

SuperClass s1 = new SuperClass();

s1.foo();                          // OK!

SuperClass s2 = new Subclass();

s2.foo();                          // What happens now?

SuperClass s3 = OtherClass.someMethod();

s3.foo();                          // What happens now?

s2.foo()の宣言された型で が許可されるかどうかに基づいて決定すると、 の抽象化境界の外側からs2のメソッドの呼び出しが許可されます。 privateSubclass

参照するオブジェクトの実際の型に基づいて判断すると、s2静的にアクセス チェックを行うことができません。ケースはs3これをさらに明確にします。コンパイラは、返されるオブジェクトの実際の型がどうなるかを知る方法がまったくありませsomeMethodん。

実行時例外が発生する可能性のあるアクセス チェックは、Java アプリケーションのバグの主な原因となります。ここで議論されている言語制限は、この厄介な問題を回避します。

于 2013-07-07T07:39:19.433 に答える
8

スーパー クラスで既に追加のアクセスを許可しているため、アクセスを制限できません。例えば

SuperClass sc = new SubClass();
sc.foo(); // is package local, not private.

のアクセスは、それが何を参照するかではなくsc、参照の型によって決定されscます。これは、実行時にコンパイラがオブジェクトの型をすべての場合に知ることは不可能であるためです。これが安全な仮定であるためには、サブクラスは親によって与えられたコントラクトを尊重する必要があります。そうしないと、有効なサブクラスになれません。これは、メソッドが実装されていると言っている親と同じですが、サブクラスは実装されていない (またはアクセスできない) と言っています。

直接ではなく、親を介してのみサブクラスのメソッドにアクセスできると言うことで、これを回避できます。これの問題は、親がいつメソッドを追加するかわからないことと、メソッドprivateをプライベートにして別の方法でアクセスできないようにするためにメソッドを作成するときです。

ところで、リフレクションを介してプライベート メソッドにアクセスすることはできますが、これには、JVM にあらゆる種類の問題を引き起こすという副作用があります。たとえば、通常どおり呼び出す方法がないと判断した場合でも、プライベート メソッドを保持する必要があります。

要するに、コードが言うことを意味するコードが必要であり、個性が分かれているわけではありません。それはパッケージローカルであるか、プライベートであり、その中間ではありませんが、実際にはどちらでもありません。これは、他の方法ではそれほど問題ではありません。つまり、サブクラスが public の場合です。より多くのメソッドを実装できるのと同じように、サブクラスが親よりも多くの場所で使用できることを意味します。

于 2013-07-07T07:39:54.520 に答える
7

これが許可されると、アクセスできないメソッドを呼び出すことができるバックドアが存在することになります。

これは許可されているとしましょう

class Super {  
    public void method() {  
        System.out.println("Super");  
    }  
}


class Sub extends Super {  
    // This is not allowed, but suppose it was allowed  
    protected void method() {  
        System.out.println("Sub");  
    }  
}

// In another class, in another package:  
Super obj = new Sub();  
obj.method();

obj.methodクラス Superにmethod()ある ため、可能です。publicしかし、obj は実際には Sub のインスタンスを参照しており、そのクラスではメソッドが保護されているため、許可されるべきではありません!

クラス Sub 内のメソッドの呼び出しを外部からアクセスできないように制限するために、この制限が付けられます。

于 2013-07-07T07:47:45.767 に答える
1

スーパー クラス メソッドのアクセス修飾子を制限することは無効なオーバーライドです。これは、スーパークラスの契約を破り、置換の原則、つまりサブクラス オブジェクトがスーパークラス オブジェクトでもあることを無効にするためです。

public void doSomething(SuperClass sc) {
  sc.publicMethodInSuperClass();
}

doSomething(new SubClass()); // would break

これが許可されている場合、SubClassにそのメソッドpublicがないため、上記のクライアント コードは機能しなくなります。

参考:
リスコフ置換原理

于 2013-07-07T07:41:14.083 に答える
0

簡単に言えば、コンパイラの作成者がこのように機能するようにルールを設定したということだと思います。LSP は当面の問題とは何の関係もありません。

この制限があると考えることができる唯一の理由は、サブクラスがインターフェイスから派生する場合、クライアント プログラマーとして、派生クラスへの参照からインターフェイスのすべてのアクセス可能なメソッドを呼び出せることを期待しているからです。

OPが示したコードを書くことができると仮定してください。派生クラスへの参照がある場合は、派生クラスのパブリック メンバーを呼び出すことができるはずです (ただし、この場合はありません)。ただし、基本クラスへの参照を受け取るメソッドに参照をパラメーターとして渡すと、メソッドはパブリック メソッドまたはパッケージ メソッド ( foo. これは、他の貢献者が探している LSP です!

C++ の例:

class Superclass{
public:
virtual void foo(){ cout << "Superclass.foo" << endl; }
};

class Subclass: public Superclass{
virtual void foo(){ cout << "Subclass.foo" << endl; }
};

int main(){
Superclass s1;
s1.foo() // Prints Superclass.foo

Subclass s2;
// s2.foo();  // Error, would not compile

Superclass& s1a=s2; // Reference to Superclass 'pointing' to Subclass

s1a.foo(); // Compiles OK, Prints Subclass.foo()
}
于 2013-07-07T13:51:16.517 に答える
0

そのような構造を使用する際の明らかな問題は別として (Peter Lawrey が彼の回答で指摘したように)、その背後にある理論についても読んでください: LSP。これは、メインの型をそのサブクラスに置き換えることができなければならないことを意味します。

于 2013-07-07T07:40:16.717 に答える
0

動的メソッド ディスパッチでは、オーバーライドされたメソッドの呼び出しは、コンパイル時ではなく実行時に解決されます。呼び出し時に参照されているオブジェクトに基づいています...

ここで、より弱いアクセス権限が許可され、別のクラスのコードに次のステートメントを記述したとします。

Superclass ref=new Subclass();
ref.foo()

実行時に Java が statement に遭遇すると、...ref.foo()を呼び出す必要がありますが、サブクラスのメソッドはコード内でプライベートとして宣言されており、プライベートは独自のクラスの外部で呼び出すことはできません..したがって、競合が発生し、実行時例外が発生します...foo()Subclassfoo()

于 2013-08-29T13:47:31.903 に答える