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


25

以下は擬似コードです。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パラダイムでこれを行うことができるのはなぜですか?


6
なぜそうではないのですか?Javaでは、プライベートメンバーはインスタンスに対してプライベートではなく、ソースファイルに対してプライベートです。最初に思い浮かぶのはequals、別のインスタンスのプライベートフィールドをチェックする必要があることです。(これは短いため、コメントとして投稿します。このアプローチのOOP性については何もありません)
Ordous 14

2
静的メソッドにはがないthisので、取得できる独自のクラスのオブジェクトは、自身で作成する(またはパラメーターとして渡される)オブジェクトのみです。したがって、これをカプセル化の違反またはセキュリティホールと見なす場合、それが非常に大きなものであるとは言えず、差し込む価値がないかもしれません。
キリアンフォス14

4
これがなぜ投票されたのかはわかりません。質問はささいなことかもしれませんが、OPは尋ねる前に2つの言語で動作をテストするという問題を経験しました。それは、私たちが通常新人から見るよりもはるかに多くの努力です。
ヤニス14

1
@YannisRizos Agreeed、そして実際、この質問は些細なことではないと思います。「最小特権の原則」に従うことに意味があります。インスタンスの内部にアクセスする必要のないヘルパー関数は別のクラスで定義する必要があり、逆にこの規則に従うと、同じクラス内に静的メソッドが存在するときはいつでも内部状態にアクセスしていることがわかります。
ドーバル14

1
実際、私が同僚に尋ねたとき、全員がこれは不可能だと言った。それが私がそれを些細なことだとは思わなかった理由です
ベン14

回答:


17

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

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

ただし、静的メソッドやそのクラスの他のインスタンスからでもクラスの実装の詳細を非表示にしたい場合は、プライベートクラスデータパターンに従うことができます。クラスのすべてのプライベート変数をプライベート内部クラスに入れ、ゲッターまたはセッターをその内部クラスのゲッターおよびセッターに委任します。

別のオプションは、クラスのすべてのパブリックメソッドを宣言するクラスのインターフェイスを定義し、そのインターフェイスの下で可能な限りクラスを参照することです。interface-typeへの参照を使用して、インターフェイスで宣言されていないものに直接アクセスすることはできません(どこでも(もちろん、リフレクションを除く)。インターフェイスを持たないオブジェクト指向プログラミング言語(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
    }

}

プライベートクラスのデータパターンが役立つ状況を考えることができますか?個人的には、内部クラスをセットアップ(Swingなど)のようなGUIで使用するか、コーディング演習で静的内部クラスを使用するのは、複数のソースファイルにまたがる演習を望まないからです。
情報14

1
クラスの内部を他のインスタンスから隠すことは、クラスがインターフェイスよりも優れている点の1つを取り戻し、何も取り戻すことはありません。よりシンプルで柔軟なソリューションは、インターフェイスを使用することです。
ドーバル14

3

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

クラス内のコードがインスタンスのプライベートメンバーにアクセスできる最大の利点は、そのアクセスレベルが適切な場合があることです。privateその方法で作業することで、その目的に使用できる別のアクセス修飾子を持つ必要がなくなります。それ以外の場合は、コードが強制的にメンバーを公開します。

(Microsoft Common Object Model(COM)の場合のように)そのようなアクセスを許可しないことの利点は、外部コードがクラスをインターフェイスとして扱うことができることです。クラスImmutableMatrixにプライベートまたは保護されたdouble[][]バッキングフィールドが含まれ、クラス内のコードが他のインスタンスのバッキング配列を調べる場合、外部コードが使用できる非配列バッキングクラス(たとえばZeroMatrixIdentityMatrix)を定義することはできません。Immutable2dMatrixそのクラスは、バッキングフィールドを含むすることなく、。内でImmutable2dMatrixが以外のインスタンスのプライベートメンバーを使用しない場合thisは、クラスの名前をに変更し、前述の非配列支援クラスをサブタイプとして持つことができるImmutableArrayBackedMatrix新しい抽象ImmutableMatrixクラスを定義できますImmutableArrayBackedMatrix

そのようなリファクタリングは、言語がその能力を利用して実際に外部インスタンスを調べない限り、言語がImmutableMatrix以外のインスタンスのバッキング配列を「許可」することで防止されないことに注意してくださいthis。言語がそのような使用を制限することの主な効果は、そのようなリファクタリングを受け入れられないコードを書くどんな試みでもすぐにコンパイラーをしゃがむようにすることです。


2

Javaは厳密にオブジェクト指向言語ではなく、クラスベースの言語です。クラスはインスタンスではなく、操作と動作のアクセスを決定します。

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.