197

オブジェクトのような構造体を作成するJavaの方法に完全に反していますか?

class SomeData1 {
    public int x;
    public int y;
}

アクセサーとミューテーターが Java に似ているクラスを確認できます。

class SomeData2 {
    int getX();
    void setX(int x);

    int getY();
    void setY(int y);

    private int x;
    private int y;
}

最初の例のクラスは表記上便利です。

// a function in a class
public int f(SomeData1 d) {
    return (3 * d.x) / d.y;
}

これはあまり便利ではありません。

// a function in a class
public int f(SomeData2 d) {
    return (3 * d.getX()) / d.getY();
}
4

20 に答える 20

298

多くの Java 関係者は、Java が「構造体」をサポートしている場合 (動作がない場合)、クラスが本質的に「構造体」である場合に public インスタンス変数を使用することが非常に適切であると述べている Sun Java コーディング ガイドラインに精通していないようです。

ゲッターとセッターは Java の中心であるかのように、Java の流儀であると考える傾向があります。そうではありません。Sun Java コーディング ガイドラインに従い、適切な状況で public インスタンス変数を使用する場合、不要な getter と setter で雑然とするよりも実際に優れたコードを記述できます。

1999 年からの Java Code Conventions であり、現在も変更されていません。

10.1 インスタンス変数とクラス変数へのアクセスの提供

正当な理由がない限り、インスタンスまたはクラス変数を public にしないでください。多くの場合、インスタンス変数を明示的に設定または取得する必要はありません。これは、メソッド呼び出しの副作用として発生することがよくあります。

適切なパブリック インスタンス変数の 1 つの例は、クラスが本質的に動作のないデータ構造である場合です。つまり、クラスの代わりに構造体を使用する場合 (Java が構造体をサポートしている場合) は、クラスのインスタンス変数を public にするのが適切です。

http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-137265.html#177

http://en.wikipedia.org/wiki/Plain_old_data_structure

http://docs.oracle.com/javase/1.3/docs/guide/collections/designfaq.html#28

于 2011-12-19T18:29:21.810 に答える
224

本当に常識を使ってください。あなたが次のようなものを持っている場合:

public class ScreenCoord2D{
    public int x;
    public int y;
}

次に、それらをゲッターとセッターでラップすることにはほとんど意味がありません。x、y座標をピクセル全体に他の方法で保存することは決してありません。ゲッターとセッターはあなたを遅くするだけです。

一方、次のようになります。

public class BankAccount{
    public int balance;
}

将来のある時点で、残高の計算方法を変更したい場合があります。これは実際にはゲッターとセッターを使用する必要があります。

ルールを曲げても問題がないことを知るために、なぜ良い習慣を適用しているのかを知ることは常に望ましいことです。

于 2008-08-31T14:44:57.457 に答える
62

これはよく議論されるトピックです。オブジェクトに public フィールドを作成することの欠点は、それに設定されている値を制御できないことです。多くのプログラマーが同じコードを使用するグループ プロジェクトでは、副作用を避けることが重要です。さらに、フィールドのオブジェクトのコピーを返したり、何らかの方法で変換したりする方が良い場合もあります。テストでそのようなメソッドをモックできます。新しいクラスを作成すると、すべての可能なアクションが表示されない場合があります。それは防御的プログラミングのようなものです - いつかゲッターとセッターが役立つかもしれません、そしてそれらを作成/使用するのに多くの費用はかかりません. したがって、それらは時々役立ちます。

実際には、ほとんどのフィールドには単純なゲッターとセッターがあります。可能な解決策は次のようになります。

public property String foo;   
a->Foo = b->Foo;

更新: プロパティのサポートが Java 7 で追加される可能性はほとんどありません。Groovy、Scala などの他の JVM 言語は、現在この機能をサポートしています。- アレックス・ミラー

于 2008-08-31T09:50:48.210 に答える
53

可変性の問題に対処するために、x と y を final として宣言できます。例えば:

class Data {
  public final int x;
  public final int y;
  public Data( int x, int y){
    this.x = x;
    this.y = y;
  }
}

これらのフィールドに書き込もうとするコードを呼び出すと、「フィールド x は final として宣言されているため、割り当てられません」というコンパイル時エラーが発生します。

クライアントコードは、投稿で説明した「簡単な」利便性を持つことができます

public class DataTest {
    public DataTest() {
        Data data1 = new Data(1, 5);
        Data data2 = new Data(2, 4);
        System.out.println(f(data1));
        System.out.println(f(data2));
    }

    public int f(Data d) {
        return (3 * d.x) / d.y;
    }

    public static void main(String[] args) {
        DataTest dataTest = new DataTest();
    }
}
于 2008-09-02T01:51:58.233 に答える
12

publicフィールドを使用しない

publicクラスの内部動作を本当にラップしたい場合は、フィールドを使用しないでください。例を挙げてみましょうjava.io.BufferedReader。次のフィールドがあります。

private boolean skipLF = false; // If the next character is a line feed, skip it

skipLFすべての read メソッドで読み書きされます。別のスレッドで実行されている外部クラスskipLFが、読み取りの途中で の状態を悪意を持って変更した場合はどうなるでしょうか? BufferedReader間違いなく暴走します。

publicフィールドを使用する

Point例として、次のクラスを取り上げます。

class Point {
    private double x;
    private double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double getX() {
        return this.x;
    }

    public double getY() {
        return this.y;
    }

    public void setX(double x) {
        this.x = x;
    }

    public void setY(double y) {
        this.y = y;
    }
}

これにより、2 点間の距離の計算を書くのが非常に困難になります。

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.getX() - a.getX(), 2) + Math.pow(b.getY() - a.getY(), 2));

このクラスには、プレーンなゲッターとセッター以外の動作はありません。クラスが単なるデータ構造を表し、動作を持たず、今後も持たない場合は、パブリック フィールドを使用することは許容されます(ここでは、シン ゲッターとセッターは動作とは見なされません)。これは次のように書くとより良くなります:

class Point {
    public double x;
    public double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
}

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));

掃除!

ただし、覚えておいてください: クラスに動作が存在しない必要があるだけでなく将来も動作する理由があってはなりません。


(これはまさにこの回答の説明です。 「Javaプログラミング言語のコード規則:10.プログラミングの実践」を引用するには:

適切なパブリック インスタンス変数の 1 つの例は、クラスが本質的に動作のないデータ構造である場合です。つまり、structクラスの代わりに を使用する場合 (Java が をサポートしている場合struct) は、クラスのインスタンス変数を public にするのが適切です。

したがって、公式ドキュメントもこの慣行を受け入れています。)


Pointまた、上記のクラスのメンバーが不変であることを確信している場合は、それfinalを強制するキーワードを追加できます。

public final double x;
public final double y;
于 2014-09-25T06:48:38.170 に答える
8

ちなみに、例として示している構造は、Java基本クラスライブラリにとしてすでに存在していますjava.awt.Point。パブリックフィールドとしてxとyがありますので、自分でチェックしてください

あなたが何をしているのかを知っていて、あなたのチームの他の人がそれを知っているなら、パブリックフィールドを持っていても大丈夫です。ただし、スタックに割り当てられた構造体であるかのようにオブジェクトを使用する開発者に関連するバグのように頭痛の種になる可能性があるため、これに依存しないでください(Javaオブジェクトは常にコピーではなく参照としてメソッドに送信されます)。

于 2008-08-31T14:24:06.710 に答える
8

Re: aku, izb, John Topley...

可変性の問題に注意してください...

ゲッター/セッターを省略するのは理にかなっているように見えるかもしれません。場合によっては、実際には問題ない場合もあります。ここに示されている提案されたパターンの本当の問題は、可変性です。

問題は、非最終的なパブリック フィールドを含むオブジェクト参照を渡すことです。その参照を持つ他のものは、それらのフィールドを自由に変更できます。そのオブジェクトの状態を制御できなくなります。(文字列がミュータブルだったらどうなるか考えてみてください。)

そのオブジェクトが別のオブジェクトの内部状態の重要な部分である場合、それは悪くなります。内部実装を公開したばかりです。これを防ぐには、代わりにオブジェクトのコピーを返す必要があります。これは機能しますが、大量の使い捨てコピーが作成されるため、GC に大きな負荷がかかる可能性があります。

public フィールドがある場合は、クラスを読み取り専用にすることを検討してください。フィールドをパラメーターとしてコンストラクターに追加し、フィールドを final としてマークします。それ以外の場合は、内部状態を公開していないことを確認し、戻り値の新しいインスタンスを作成する必要がある場合は、過度に呼び出されないようにしてください。

参照: Joshua Bloch による「Effective Java」 -- 項目 #13: 不変性を優先する。

PS: また、最近のすべての JVM は、可能であれば getMethod を最適化して排除し、フィールド読み取り命令が 1 つになることにも注意してください。

于 2008-08-31T16:27:29.707 に答える
7

私はこれをいくつかのプロジェクトで試しました。getter と setter がコードを意味論的に無意味なもので乱雑にし、他の言語は規約に基づいたデータ隠蔽または責任の分割 (Python など) で問題なく動作するように見えるという理論に基づいています。

他の人が上で指摘したように、あなたが遭遇する2つの問題があり、それらは実際には修正できません:

  • Java の世界のほぼすべての自動化ツールは、getter/setter 規則に依存しています。他の人が指摘したように、jspタグ、スプリング構成、Eclipseツールなどについても同様です...ツールが期待するものと戦うことは、非標準的な開始方法を見つけようとしてGoogleをトローリングする長いセッションのレシピです春豆。本当に面倒な価値はありません。
  • 数百のパブリック変数を使用してエレガントにコード化されたアプリケーションを作成すると、それらが不十分な状況が少なくとも1つ見つかる可能性があります-絶対に不変性が必要な場合、または変数が設定されたときに何らかのイベントをトリガーする必要がある場合、またはスローしたい場合オブジェクトの状態を不快なものに設定するため、変数の変更の例外。その場合、変数が直接参照されるすべての場所で特別なメソッドを使用してコードを乱雑にするか、アプリケーション内の 1000 個の変数のうち 3 つに特別なアクセス フォームを使用するかという、うらやましい選択に行き詰まります。

これは、完全に自己完結型のプライベート プロジェクトで作業する場合の最良のシナリオです。すべてを公的にアクセス可能なライブラリにエクスポートすると、これらの問題はさらに大きくなります。

Java は非常に冗長であり、これは魅力的なことです。やらないでください。

于 2008-12-01T20:50:48.187 に答える
4

Java の方法が OO の方法である場合、はい、パブリック フィールドを持つクラスを作成すると、オブジェクトが独自の内部状態を管理する必要があるという情報隠蔽に関する原則が破られます。(つまり、専門用語を吐き出しているだけではないので、情報隠蔽の利点は、クラスの内部動作がインターフェイスの背後に隠されることです。たとえば、構造体クラスがそのフィールドの 1 つを保存するメカニズムを変更したいとします。おそらく戻って、そのクラスを使用するクラスを変更する必要があります...)

また、JavaBean 命名規則に準拠したクラスのサポートを利用することもできません。これは、たとえば、式言語を使用して記述された JavaServer Page でクラスを使用することにした場合に問題になります。

JavaWorld の記事Why Getter and Setter Methods are Evilの記事も、いつアクセサー メソッドとミューテーター メソッドを実装しないかを考える際に役立つかもしれません。

小さなソリューションを作成していて、関連するコードの量を最小限に抑えたい場合、Java の方法は正しい方法ではない可能性があります。それは常に、あなたと解決しようとしている問題に依存していると思います。

于 2008-08-31T09:33:52.383 に答える
3

作成者がオブジェクトではなく構造体 (またはデータ シャトル) であることを知っていれば、そのタイプのコードに問題はありません。多くの Java 開発者は、整形式のオブジェクト (単なる java.lang.Object のサブクラスではなく、特定のドメイン内の真のオブジェクト) とパイナップルの違いを見分けることができません。したがって、オブジェクトが必要なときに構造体を書くことになり、その逆もあります。

于 2010-04-15T02:59:08.510 に答える
2

コードを単純化するためにプライベート内部クラスを構築するときにこのパターンを頻繁に使用しますが、パブリックAPIでそのようなオブジェクトを公開することはお勧めしません。一般に、パブリックAPIのオブジェクトを不変にすることができる頻度が高いほど、「構造体のような」オブジェクトを不変に構築することはできません。

余談ですが、このオブジェクトをプライベート内部クラスとして記述している場合でも、オブジェクトを初期化するためのコードを単純化するコンストラクターを提供します。使用可能なオブジェクトを取得するために3行のコードが必要になるのは、面倒です。

于 2008-10-15T04:28:04.103 に答える
2

public フィールド アクセスを使用する場合の問題は、ファクトリ メソッドの代わりに new を使用する場合と同じ問題です。後で気が変わった場合、既存の呼び出し元はすべて壊れてしまいます。そのため、API の進化という観点からは、通常は、気を抜いてゲッター/セッターを使用することをお勧めします。

たとえば、内部データ構造として使用される内部静的クラスで、クラスへのアクセスを強力に制御する場合です。この場合、フィールド アクセスを使用する方がはるかに明確な場合があります。

ちなみに、e-bartek の主張によると、プロパティのサポートが Java 7 に追加される可能性は非常に低いです。

于 2008-09-15T16:21:20.450 に答える
1

これはオブジェクト指向設計に関する質問であり、Java言語ではありません。一般に、クラス内のデータ型を非表示にし、クラスAPIの一部であるメソッドのみを公開することをお勧めします。内部データ型を公開する場合、将来それらを変更することはできません。それらを非表示にした場合、ユーザーに対する唯一の義務は、メソッドの戻り値と引数の型です。

于 2008-08-31T10:13:01.230 に答える
1

それが常に単純な構造体になり、それに動作を追加したくないことがわかっていれば、害はありません。

于 2008-08-31T09:20:18.080 に答える
1

ここでは、5 人の異なる人物の名前と年齢を入力し、選択ソート (年齢ごと) を実行するプログラムを作成します。構造体として機能するクラス (C プログラミング言語など) とメイン クラスを使用して、完全な操作を実行しました。以下にコードを提供しています...

import java.io.*;

class NameList {
    String name;
    int age;
}

class StructNameAge {
    public static void main(String [] args) throws IOException {

        NameList nl[]=new NameList[5]; // Create new radix of the structure NameList into 'nl' object
        NameList temp=new NameList(); // Create a temporary object of the structure

        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

        /* Enter data into each radix of 'nl' object */

        for(int i=0; i<5; i++) {
            nl[i]=new NameList(); // Assign the structure into each radix

            System.out.print("Name: ");
            nl[i].name=br.readLine();

            System.out.print("Age: ");
            nl[i].age=Integer.parseInt(br.readLine());

            System.out.println();
        }

        /* Perform the sort (Selection Sort Method) */

        for(int i=0; i<4; i++) {
            for(int j=i+1; j<5; j++) {
                if(nl[i].age>nl[j].age) {
                    temp=nl[i];
                    nl[i]=nl[j];
                    nl[j]=temp;
                }
            }
        }

        /* Print each radix stored in 'nl' object */

        for(int i=0; i<5; i++)
            System.out.println(nl[i].name+" ("+nl[i].age+")");
    }
}

上記のコードはエラーフリーでテスト済みです...コピーしてIDEに貼り付けるだけで...ご存知のとおり、何ですか??? :)

于 2013-02-19T05:50:04.413 に答える
0

Javaでは、パブリックフィールドを使用し、メソッドを使用しない単純なクラスを作成できますが、それでもクラスであり、構文上、およびクラスと同じようにメモリ割り当ての観点から処理されます。Javaで構造体を真に再現する方法はありません。

于 2008-10-15T04:35:09.423 に答える
0

アスペクト指向プログラミングを使用すると、代入またはフェッチをトラップしてインターセプト ロジックをそれらにアタッチできます。これが問題を解決する正しい方法だと私は提案します。(公開するか、保護するか、パッケージで保護するかという問題は、直交しています。)

したがって、適切なアクセス修飾子を持つインターセプトされていないフィールドから始めます。プログラムの要件が大きくなるにつれて、ロジックをアタッチして、おそらく検証したり、返されるオブジェクトのコピーを作成したりします。

ゲッター/セッターの哲学は、コストが必要でない多数の単純なケースにコストを課します。

アスペクト スタイルがきれいかどうかは、ある程度質的なものです。クラス内の変数だけを表示し、ロジックを個別に表示するのは簡単だと思います。実際、Apect 指向プログラミングの存在理由は、多くの懸念事項が分野横断的であり、それらをクラス本体自体で区分化することは理想的ではないということです (ロギングは例です。Java が要求するすべての取得をログに記録したい場合)。大量のゲッターを作成して同期を維持する必要がありますが、AspectJ ではワンライナーが可能です)。

IDE の問題は厄介な問題です。get/set から発生するのは、読み取りと視覚的な汚染であるため、タイピングではありません。

アノテーションは一見アスペクト指向プログラミングに似ていますが、AspectJ の簡潔なワイルドカードのようなポイントカット仕様とは対照的に、アノテーションを付けることによってポイントカットを徹底的に列挙する必要があります。

AspectJ の認識が、人々が時期尚早に動的言語に落ち着くのを防ぐことを願っています。

于 2011-12-19T19:02:53.247 に答える
0

ほとんどの場合と同様に、一般的なルールと特定の状況があります。特定のオブジェクトがどのように使用されるかを把握できるように、クローズドでキャプチャされたアプリケーションを実行している場合は、可視性や効率性を優先する自由度を高めることができます。あなたの制御を超えて他の人によって公に使用されるクラスを開発している場合は、ゲッター/セッター モデルに傾倒してください。すべてのことと同様に、常識を働かせてください。多くの場合、パブリックで最初のラウンドを行い、後でゲッター/セッターに変更しても問題ありません。

于 2010-04-25T04:13:52.203 に答える
0

メソッドから複数の値を返す必要があるときに、このようなクラスを使用することがあります。もちろん、そのようなオブジェクトは寿命が短く、可視性が非常に限られているため、問題はありません。

于 2008-11-23T18:43:14.950 に答える