web-dev-qa-db-ja.com

静的メソッドでプライベートメンバーにアクセスできるのはなぜですか?

以下は疑似コードで、JavaおよびPHPで試してみましたが、どちらも機能しました。

class Test { 

    private int a = 5;

    public static function do_test(){
        var t = new Test();
        t.a = 1;
        print t.a // 1
    }

}

Test::do_test();

OOPパラダイムでこれを行うことができるのはなぜですか、それはどのように使用されますか?

25
Ben

Javaでは、プライベート変数はクラス全体に表示されます。これらは、静的メソッドおよび同じクラスの他のインスタンスからアクセスできます。

これは、たとえば factory methods で役立ちます。ファクトリメソッドは通常、アプリケーションコードに任せたくないほど複雑なオブジェクトの初期化を行います。初期化を行うために、ファクトリメソッドは、公開したくないクラス内部へのアクセスを必要とすることがよくあります。プライベート変数に直接アクセスできることで、あなたの人生はずっと簡単になります。

ただし、静的メソッドやそのクラスの他のインスタンスからでもクラスの実装の詳細を非表示にする場合は、 private class data pattern を使用できます。クラスのすべてのプライベート変数をプライベート内部クラスに配置し、ゲッターまたはセッターをその内部クラスのゲッターおよびセッターに委任します。

別のオプションは、クラスのすべてのパブリックメソッドを宣言するクラスのインターフェイスを定義し、そのインターフェイスの下のクラスを可能な限り参照することです。インターフェースタイプへの参照を使用して、インターフェースで宣言されていないものに直接アクセスすることはできません(もちろん、リフレクションを除く)。インターフェースを持たないオブジェクト指向プログラミング言語(C++など)を使用する場合、実際のクラスによって継承される抽象基本クラスを使用してそれらをシミュレートできます。

interface ITest {
     public int getA();
}

class Test implements ITest { 

    private int a = 5;

    public int getA() { return a; } // implementation of method declared in interface

    public static void main(){
        ITest t = new Test();
        t.a = 1; // syntax error: Interface ITest has no "a"
        System.out.println(t.getA()); // calls Test.getA, visible because ITest declares it
    }

}
17
Philipp

一部の言語およびランタイムフレームワーク(Java、.NETなど)は、特定のクラスのコードをコンパイルしているすべての人が、そのクラスのインスタンスのプライベートメンバーを、その正しい方法に有害な方法で使用しないように信頼できると想定しています。操作。他の言語やフレームワークはその点でより制限的であり、そのインスタンスでを実行するコードによる場合を除いて、インスタンスのプライベートメンバーへのアクセスを許可しません。どちらの設計にも長所と短所があります。

クラス内の任意のコードが任意のインスタンスのプライベートメンバーにアクセスできるようにする最大の利点は、そのレベルのアクセスが適切な場合があり、そのようにprivateを機能させることで、その目的で異なるアクセス修飾子を使用する必要がなくなります。または、コードを強制して、そうでなければ理想的であるよりも広くメンバーを公開します。

このようなアクセスを禁止する利点(Microsoft共通オブジェクトモデル(COM)の場合と同様)は、外部コードがクラスをインターフェイスとして扱うことができることです。クラスImmutableMatrixにプライベートまたは保護されたdouble[][]バッキングフィールドが含まれていて、クラス内のコードが他のインスタンスのバッキング配列を検査する場合、配列にバッキングされていないクラス(たとえば、ZeroMatrixIdentityMatrix)外部コードがImmutable2dMatrixとして使用でき、そのクラスにバッキングフィールドを含める必要はありません。 Immutable2dMatrix内でthis以外のインスタンスのプライベートメンバーを使用していない場合は、クラスの名前をImmutableArrayBackedMatrixに変更し、ImmutableMatrixと前述の非配列-を持つことができる新しい抽象ImmutableArrayBackedMatrixクラスを定義できます。サブタイプとしての支援クラス。

このようなリファクタリングは、言語がImmutableMatrix以外のインスタンスのバッキング配列を「許可」thisで検査しても阻止されないことに注意してください。ただし、言語がその機能を利用して実際に外部インスタンスを検査している場合を除きます。言語がそのような使用を制限することの主な効果は、そのようなリファクタリングを受け入れられないコードを書こうとするあらゆる試みで、コンパイラーがすぐに不規則になることです。

3
supercat

Javaは厳密にはオブジェクト指向言語ではありませんが、 クラスベースの言語 -クラスでは、インスタンスではなく操作と動作のアクセスが決定されます。

したがって、厳密にオブジェクト指向ではないことを実行できることに驚かないでください。

メソッドはインスタンスと同じクラススコープ内にあるため、プライベートメンバーへのフルアクセスが可能です。同様のルールが、外部クラスのインスタンスからデータにアクセスする内部クラスのインスタンスを管理します。内部クラスのインスタンスは、外部クラスのプライベートメンバーにアクセスできます。

これはC++から継承され、コピーおよび移動コンストラクターの作成に役立ちます。また、効率的に公開されていないメンバーに値が依存する2つのオブジェクトを比較または組み合わせる場合にも役立ちます(たとえば、Javaの配列のゲッターは配列をコピーする必要があるため、クライアントコードはオブジェクトを変更できないため、オブジェクトの内部状態を変更できますが、オブジェクトの等価性を比較するために配列をコピーする必要は効率的ではありません)

2
Pete Kirkham