質問
オブジェクトのような構造体を作成する 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();
}
解決
これはよく議論されるトピックです。オブジェクト内にパブリック フィールドを作成する場合の欠点は、それに設定される値を制御できないことです。多くのプログラマーが同じコードを使用するグループ プロジェクトでは、副作用を避けることが重要です。さらに、フィールドのオブジェクトのコピーを返すか、何らかの方法で変換した方がよい場合もあります。テスト内でそのようなメソッドをモックできます。新しいクラスを作成した場合、可能なアクションがすべて表示されない可能性があります。これは防御的なプログラミングのようなものです。ゲッターとセッターはいつか役立つかもしれません。また、それらを作成/使用するのにそれほど費用はかかりません。したがって、それらは時々役立つことがあります。
実際には、ほとんどのフィールドには単純なゲッターとセッターがあります。考えられる解決策は次のようになります。
public property String foo;
a->Foo = b->Foo;
アップデート:プロパティのサポートが Java 7 に追加される可能性は非常に低く、おそらく今後も追加される可能性はありません。Groovy、Scala などの他の JVM 言語は現在この機能をサポートしています。- アレックス・ミラー
他のヒント
多くのJavaの人々は、クラスが本質的に「構造体」である場合、「構造」をサポートしている場合(動作がない場合)、パブリックインスタンス変数を使用することが非常に適切であると言うSun Javaコーディングガイドラインに精通していないようです。
人々は、ゲッターとセッターがジャワの中心にいるかのようにジャワの方法だと考える傾向があります。そうではありません。適切な状況でパブリックインスタンス変数を使用して、Sun Javaコーディングガイドラインに従うと、実際には不必要なゲッターとセッターで散らかっているよりも優れたコードを書いています。
1999 年からの Java コード規約 そして今も変わらず。
10.1 インスタンス変数とクラス変数へのアクセスの提供
正当な理由がない限り、インスタンスまたはクラス変数をパブリックにしないでください。多くの場合、インスタンス変数を明示的に設定または取得する必要はありません。これはメソッド呼び出しの副作用として発生することがよくあります。
適切なパブリック インスタンス変数の一例は、クラスが本質的に動作を持たないデータ構造である場合です。 言い換えれば、クラスの代わりに構造体を使用する場合 (Java が構造体をサポートしている場合)、クラスのインスタンス変数をパブリックにするのが適切です。.
http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-137265.html#177
http://en.wikipedia.org/wiki/Plain_old_data_structor
http://docs.oracle.com/javase/1.3/docs/guide/collections/designfaq.html#28
本当に常識を働かせてください。次のようなものがある場合:
public class ScreenCoord2D{
public int x;
public int y;
}
その場合、それらをゲッターとセッターでラップすることにほとんど意味がありません。他の方法では、x、y 座標をピクセル全体で保存することはできません。ゲッターとセッターは速度を低下させるだけです。
一方、次のようになります。
public class BankAccount{
public int balance;
}
将来のある時点で、残高の計算方法を変更することが必要になる場合があります。これは実際にはゲッターとセッターを使用する必要があります。
常に知っておくことが望ましい なぜ 適切な実践を実践しているので、いつルールを曲げても大丈夫なのかがわかります。
可変性の問題に対処するには、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();
}
}
使ってはいけません public
田畑
使用しないでください public
本当にクラスの内部動作をラップしたい場合にフィールドを使用します。取る java.io.BufferedReader
例えば。次のフィールドがあります。
private boolean skipLF = false; // If the next character is a line feed, skip it
skipLF
すべての読み取りメソッドで読み取りおよび書き込みが行われます。別のスレッドで実行されている外部クラスが悪意を持って状態を変更した場合はどうなるでしょうか。 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.プログラミングの実践」:
適切なパブリック インスタンス変数の一例は、クラスが本質的に動作を持たないデータ構造である場合です。言い換えれば、もしあなたが
struct
クラスの代わりに (Java がサポートされている場合)struct
) の場合、クラスのインスタンス変数をパブリックにすることが適切です。
したがって、公式ドキュメントでもこの慣行が受け入れられています。)
また、上記のメンバーが確実であると確信している場合は、 Point
クラスは不変である必要があるので、追加できます final
それを強制するためのキーワード:
public final double x;
public final double y;
ちなみに、例として挙げている構造は、Javaの基本クラスライブラリにすでに存在しています。 java.awt.Point
. 。パブリックフィールドとして x と y があります。 自分で調べてください.
自分が何をしているのかを理解しており、チーム内の他の人もそれを知っている場合は、パブリック フィールドがあっても問題ありません。ただし、スタックに割り当てられた構造体であるかのようにオブジェクトを使用する開発者に関連するバグなど、頭痛の種を引き起こす可能性があるため、これに依存しないでください (Java オブジェクトは常に、コピーとしてではなく参照としてメソッドに送信されます)。
Re:アク、izb、ジョン・トップリー...
可変性の問題に注意してください...
ゲッター/セッターを省略するのが賢明だと思われるかもしれません。実際には場合によっては大丈夫かもしれません。ここで提案されているパターンの本当の問題は、可変性です。
問題は、最終ではないパブリック フィールドを含むオブジェクト参照を渡した場合です。その参照を持つ他のものはすべて、それらのフィールドを自由に変更できます。そのオブジェクトの状態を制御することはできなくなります。(文字列が変更可能である場合に何が起こるかを考えてください。)
そのオブジェクトが別のオブジェクトの内部状態の重要な部分であり、内部実装を公開したばかりの場合は問題が発生します。これを防ぐには、代わりにオブジェクトのコピーを返す必要があります。これは機能しますが、大量の使い捨てコピーが作成されるため、大規模な GC プレッシャーが発生する可能性があります。
パブリック フィールドがある場合は、クラスを読み取り専用にすることを検討してください。フィールドをパラメーターとしてコンストラクターに追加し、フィールドを Final としてマークします。それ以外の場合は、内部状態を公開していないことを確認し、戻り値のために新しいインスタンスを構築する必要がある場合は、それが過度に呼び出されないようにしてください。
見る:」効果的なJava「ジョシュア・ブロック著 -- 項目 #13:不変性を優先します。
追伸:また、最近のすべての JVM は、可能であれば getMethod を最適化し、フィールド読み取り命令が 1 つだけになることにも注意してください。
私はこれをいくつかのプロジェクトで試しました。ゲッターとセッターは意味的に意味のない粗末なものでコードを乱雑にし、他の言語は規約に基づいたデータの隠蔽や責任の分割を問題なく行うようだという理論に基づいています。パイソン)。
他の人が上で指摘したように、遭遇する問題が 2 つあり、それらは実際には修正できません。
- Java の世界のほぼすべての自動化ツールは、ゲッター/セッター規約に依存しています。他の人が指摘したように、jsp タグ、スプリング構成、Eclipse ツールなどについても同様です。等...ツールが期待しているものと戦うことは、Spring Bean を開始する非標準的な方法を見つけようとして Google を検索する長いセッションの原因となります。本当に苦労する価値はありません。
- 数百のパブリック変数を使用してエレガントにコード化されたアプリケーションを作成すると、それらでは不十分な状況が少なくとも 1 つ見つかるでしょう。つまり、不変性が絶対に必要な場合、変数が設定されたときに何らかのイベントをトリガーする必要がある場合、または変数をスローしたい場合などです。オブジェクトの状態を不快なものに設定するため、変数変更の例外が発生します。その場合、変数が直接参照されるすべての場所で特別なメソッドを使用してコードを乱雑にするか、アプリケーション内の 1000 個の変数のうち 3 つに特別なアクセス フォームを使用するかという、うらやましい選択に悩まされることになります。
これは、完全に自己完結型のプライベート プロジェクトで作業する最良のシナリオです。全体を公的にアクセス可能なライブラリにエクスポートすると、これらの問題はさらに大きくなります。
Java は非常に冗長なので、これを実行するのは魅力的です。やめてください。
Java の方法が OO の方法である場合、はい、パブリック フィールドを持つクラスを作成することは、オブジェクトが独自の内部状態を管理する必要があるという情報隠蔽に関する原則に違反します。(専門用語を吐き出すだけではありませんが、情報隠蔽の利点は、クラスの内部動作がインターフェイスの背後に隠されることです。たとえば、構造体クラスがそのフィールドの 1 つを保存するメカニズムを変更したいとします。おそらく、そのクラスに戻って、そのクラスを使用しているクラスを変更する必要があるでしょう...)
また、JavaBean 命名準拠クラスのサポートを利用することもできません。これは、たとえば、式言語を使用して記述された JavaServer ページでクラスを使用する場合に問題となります。
JavaWorld の記事 Getter メソッドと Setter メソッドが悪である理由 この記事は、アクセサー メソッドとミューテーター メソッドを実装しない場合について考えるのにも興味深いかもしれません。
小規模なソリューションを作成していて、関連するコードの量を最小限に抑えたい場合、Java の方法は正しい方法ではない可能性があります。それは常にあなたと解決しようとしている問題に依存すると思います。
作成者が次のことを行っている限り、そのタイプのコードには何も問題はありません。 知っています これらはオブジェクトではなく構造体 (またはデータ シャトル) です。多くの Java 開発者は、整形式のオブジェクト (java.lang.Object のサブクラスだけでなく、 真実 特定のドメインのオブジェクト) とパイナップル。したがって、オブジェクトが必要な場合は構造体を書くことになり、その逆も同様です。
パブリック フィールド アクセスを使用する場合の問題は、ファクトリ メソッドの代わりに new を使用する場合と同じ問題です。後で気が変わった場合、既存の呼び出し元がすべて壊れてしまいます。したがって、API の進化の観点から見ると、通常は、思い切ってゲッター/セッターを使用することをお勧めします。
私が逆の方向に進むのは、たとえば内部データ構造として使用される内部静的クラスなど、クラスへのアクセスを強力に制御する場合です。この場合、フィールド アクセスを使用する方がはるかに明確になる可能性があります。
ちなみに、e-bartek の主張では、IMO ではプロパティのサポートが Java 7 に追加される可能性は非常に低いです。
コードを簡素化するためにプライベート内部クラスを構築するときにこのパターンをよく使用しますが、そのようなオブジェクトをパブリック API で公開することはお勧めしません。一般に、パブリック API 内のオブジェクトを不変にできる頻度が高いほど良いのですが、「構造体のような」オブジェクトを不変の方法で構築することは不可能です。
余談ですが、このオブジェクトをプライベート内部クラスとして作成していたとしても、オブジェクトを初期化するコードを簡素化するコンストラクターを提供します。使用可能なオブジェクトを取得するために 3 行のコードが必要になるのは、面倒なだけです。
非常に古い質問ですが、もう一度短い貢献をさせてください。Java 8 では、ラムダ式とメソッド参照が導入されました。ラムダ式は単純なメソッド参照である場合があり、「真の」本体を宣言することはできません。ただし、フィールドをメソッド参照に「変換」することはできません。したがって
stream.mapToInt(SomeData1::x)
合法ではありませんが、
stream.mapToInt(SomeData2::getX)
は。
常に単純な構造体になり、それに動作を付加したくないことがわかっていれば、害はないと思います。
これは Java 言語ではなく、オブジェクト指向設計に関する質問です。一般に、クラス内のデータ型を非表示にし、クラス API の一部であるメソッドのみを公開することをお勧めします。内部データ型を公開すると、将来的には変更できなくなります。それらを非表示にした場合、ユーザーに対する唯一の義務は、メソッドの戻り値と引数の型だけです。
ここでは、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 に貼り付けるだけです...知っていますか?:)
Java ではパブリック フィールドを持ち、メソッドを持たない単純なクラスを作成できますが、それは依然としてクラスであり、構文的にもメモリ割り当てに関してもクラスと同様に処理されます。Java で構造体を完全に再現する方法はありません。
メソッドから複数の値を返す必要がある場合に、このようなクラスを使用することがあります。もちろん、そのようなオブジェクトは寿命が短く、可視性が非常に限られているため、問題ないはずです。
ほとんどのことと同様、一般的なルールがあり、その後に特定の状況があります。特定のオブジェクトがどのように使用されるかを把握できるように、クローズされたキャプチャされたアプリケーションを実行している場合は、より自由に可視性や効率性を優先することができます。自分の制御できない他の人によってパブリックに使用されるクラスを開発している場合は、ゲッター/セッター モデルに頼ってください。すべてのことと同様、常識に従ってください。多くの場合、パブリックを使用して最初のラウンドを実行し、後でそれらをゲッター/セッターに変更しても問題ありません。
アスペクト指向プログラミングを使用すると、割り当てやフェッチをトラップし、それらにインターセプト ロジックを付加できます。これが問題を解決する正しい方法であると私は提案しています。(パブリックにするか、プロテクトするか、パッケージで保護するかという問題は、直交しています。)
したがって、適切なアクセス修飾子を持つインターセプトされていないフィールドから開始します。プログラムの要件が大きくなるにつれて、検証を行ったり、返されるオブジェクトのコピーを作成したりするためのロジックを追加することになります。
ゲッター/セッターの哲学では、必要のない多くの単純なケースにコストがかかります。
アスペクトスタイルがよりクリーンかどうかは、ある程度定性的なものです。クラス内の変数だけを表示し、ロジックを個別に表示するのは簡単だと思います。実際、Apet 指向プログラミングの存在理由は、多くの懸念事項が横断的であり、クラス本体自体でそれらを区分するのが理想的ではないことです (ログはその一例です。すべてをログに記録したい場合は、Java が要求するログが取得されます)。大量のゲッターを作成してそれらを同期させますが、AspectJ ではワンライナーで実行できます)。
IDE の問題は厄介な問題です。それはタイピングというよりも、get/sets から生じる読み取りと視覚的な汚染です。
アノテーションは一見アスペクト指向プログラミングに似ているように見えますが、AspectJ の簡潔なワイルドカードのようなポイントカット仕様とは対照的に、アノテーションをアタッチしてポイントカットを徹底的に列挙する必要があります。
AspectJ を認識することで、人々が動的言語に時期尚早に慣れてしまうことを防ぐことを願っています。