web-dev-qa-db-ja.com

インスタンス変数がJava常にプライベートなのはなぜですか?

私はJavaの初心者であり、カプセル化について学び、インスタンス変数がクラスでプライベートとして宣言されている例をみました。

http://www.tutorialspoint.com/Java/java_encapsulation.htm

2つのクエリがあります。

  1. インスタンス変数がプライベートなのはなぜですか?なぜ公開しないのですか?
  2. インスタンス変数が公開され、直接アクセスされるとどうなりますか?制約はありますか?

インスタンス変数がJavaのクラスでパブリックとして宣言されている場合、何がうまくいかないかについて例を挙げて説明できますか?

20
Deepak

インスタンス変数はプライベートになり、それらのクラスのユーザーにメソッドを使用してそれらにアクセスさせます。ほとんどの場合、単純なゲッターとセッターがありますが、他の方法も使用される場合があります。

たとえば、メソッドを使用すると、読み取り専用へのアクセスを制限できます。つまり、セッターがない場合、フィールドは読み取られても書き込まれません。フィールドがパブリックの場合、それは不可能です。

さらに、フィールドアクセスにチェックまたは変換を追加することもできますが、これはパブリックフィールドへのプレーンアクセスでは不可能です。フィールドがパブリックであり、後で追加のチェックなどを実行する何らかの方法ですべてのアクセスを強制する場合、そのフィールドのすべての使用法を変更する必要があります。プライベートにした場合、後でアクセス方法を変更する必要があります。

phoneがプライベートの場合:

この場合を考えてみましょう:

class Address {
  private String phone;

  public void setPhone(String phone) {
    this.phone = phone;
  }
}

//access:
Address a = new Address();
a.setPhone("001-555-12345");

このようなクラスで開始し、後でphoneNumber(たとえば、最小の長さ、数字のみなど)でチェックを実行する必要がある場合、セッターを変更する必要があります。

class Address {
  private String phone;

  public void setPhone(String phone) {
    if( !isValid( phone) ) { //the checks are performed in the isValid(...) method
     throw new IllegalArgumentException("please set a valid phone number");
    }

    this.phone = phone;
  }
}

//access:
Address a = new Address();
a.setPhone("001-555-12345"); //access is the same

phoneがパブリックの場合:

誰かがphoneを次のように設定できますが、それについては何もできません。

Address a = new Address();
a.phone="001-555-12345";

検証チェックを強制的に実行したい場合は、プライベートにする必要があり、上記の行を書いた人は2行目をこれに変更する必要があります:

a.setPhone("001-555-12345");

したがって、他のコードを壊さずにチェックを追加することはできませんでした(もうコンパイルしません)。

さらに、メソッドを介してクラスのすべてのフィールド/プロパティにアクセスする場合、アクセスの一貫性を保ち、ユーザーはプロパティが格納されているか(インスタンスフィールドであるか) )。

36
Thomas

haveはプライベートではありませんが、そうすべきです。フィールドは実装の詳細であるため、非公開にしておく必要があります。ユーザーが値を取得または設定できるようにする場合は、propertiesを使用してそのようにします(メソッドを取得および設定します)-これにより、安全に(入力の検証など)でき、変更することもできます下位互換性を失うことなく、実装の詳細(値の一部を他のオブジェクトに委任するなど)。

10
Jon Skeet

まず、すべてのインスタンス変数がプライベートであることは事実ではありません。それらの一部は保護されていますが、カプセル化は維持されます。

カプセル化の一般的な考え方は、クラスがその内部状態を公開すべきではないということです。メソッドの実行にのみ使用する必要があります。その理由は、各クラスにはいわゆる「状態空間」があるためです。つまり、フィールドの可能な値のセット。状態空間を制御できますが、公開すると、他の人が無効な状態にする可能性があります。

たとえば、2つのブール型フィールドがあり、[false、false]、[false、true]、および[true、false]の3つの場合にのみクラスが適切に機能する場合。フィールドをパブリックにすると、別のオブジェクトが内部制約を認識せずに[true、true]を設定でき、元のオブジェクトで呼び出された次のメソッドが予期しない結果をトリガーします。

7
Bozho

インスタンス変数をパブリックまたはプライベートにすることは、クラスを宣言するときに設計者が行う設計上のトレードオフです。インスタンス変数を公開することにより、クラス実装の詳細を公開することで、将来のメンテナンス作業を妨げる可能性のある費用で表現の効率と簡潔さを高めます。クラスの内部実装の詳細を非表示にすることで、そのクラスを使用するコードを壊すことなく、将来クラスの実装を変更できる可能性があります。

Oracleホワイトペーパー

4
rwyland

すでにいくつかの回答者によって指摘されているように、インスタンス変数はprivateである必要はありませんが、カプセル化を維持するために、少なくとも少なくともpublicにされることはありません。

これを非常によく説明しているClean Codeの例を見ました。正しく思い出せば、それは(a+biのような)複素数型でした。いずれにせよ、そのような非常に何か、私は本を手元に持っていません。インスタンスの値を設定するメソッドと同様に、実数部と虚数部の値を取得するメソッドを公開しました。これの大きな利点は、コードのコンシューマーを壊さずに実装を完全に置き換えることができることです。たとえば、複素数は、複素平面上の座標(a+bi)、または極座標形式(φおよび|z|)の2つの形式のいずれかで保存できます。内部ストレージ形式を実装の詳細に保つことにより、両方のフォームで数値を公開しながら前後に変更できるため、クラスのユーザーは現在実行中の操作に都合の良い方を選択できます。

他の状況では、フィールドxが特定の範囲内にある場合、フィールドyには特定のプロパティが必要であるなど、関連するフィールドのセットがあります。単純な例は、数値と任意の値xに対して、yzからy+zの範囲になければならない場合です。アクセサーとミューテーターを公開することにより、2つの値の間にこの関係を強制できます。インスタンス変数を直接公開すると、誰かが一方を設定せず他方を設定しないことを保証できない、または不変式が保持されないように設定できないため、不変式はすぐにバラバラになります。

もちろん、リフレクションを考慮すると、想定されていないメンバーにアクセスすることも可能ですが、誰かがクラスをリフレクトしてプライベートメンバーにアクセスする場合、彼らは自分のしていることが非常にうまく機能しない可能性があることに気付いたはずです。パブリックインターフェイスを使用している場合、すべてが正常であると考える可能性がありますが、特定の実装の実装の詳細を知らないうちに完全に遵守しなかったため、厄介なバグが発生します。

2
a CVn

従来のオブジェクト指向設計では、クラスはデータ(変数)と動作(メソッド)の両方をカプセル化します。プライベートデータがあると、動作の実装方法に柔軟性が与えられます。たとえば、オブジェクトに値のリストを格納し、これらの値の平均を計算して返すgetAverage()メソッドを含めることができます。後で、計算された平均をクラスで最適化してキャッシュできますが、コントラクト(つまり、メソッド)を変更する必要はありません。

過去数年で(良くも悪くも) 貧弱なデータモデル を使用することが一般的になりました。クラスはフィールドと対応するゲッターとセッターの束にすぎません。この設計では、ゲッターとセッターは実際のカプセル化を提供しないが、実際のオブジェクト指向を実行していると思わせるために、パブリックフィールドを使用する方が良いと主張します。

更新:質問のリンクに記載されている例は、この縮退カプセル化の完璧な例です。著者は簡単な例を提供しようとしていることを理解していますが、そうすることで、カプセル化の本当の利点を伝えられません(少なくともサンプルコードでは)。

1
James Scriven

クラスのユーザー向け

Eclipse、netbinsなどのideを使用している私たちは、パブリックメソッドを提案していることがわかりました。したがって、クラスの作成者がプライベートインスタンス変数のゲッターとセッターを提供する場合、変数の名前を記憶する必要はありません。セットを押すだけでCtrl +スペースを押すと、そのクラスの作成者が作成したすべてのセッターメソッドを取得し、目的のメソッドを選択して変数値を設定します。

クラスの作成者向け

変数値を設定するロジックを指定する必要がある場合があります。 「0を格納する整数変数があるとします

  1. クラスの構造を変更した場合(フィールドの削除など);バグの原因になります。ただし、getX()メソッドがある場合は、必要な値を計算できます(フィールドが削除された場合)。

  2. その場合、何かが変更されたかどうかをクラスが認識せず、整合性を保証できないという問題があります。

0
MasterCassim

フィールドをプライベートに保つことには、上で提案したように多くの利点があります。次に最適なレベルは、Javaデフォルトのアクセスレベルを使用して、パッケージをプライベートに保つことです。

デフォルトレベルは、独自のコードの乱雑さを回避し、コードのクライアントが無効な値を設定するのを防ぎます。

0
Mangoose