java.timeに、コンストラクタだけでなくオブジェクトを作成するためのメソッドがあるのはなぜですか?


8

新しいjava.timeパッケージでは、コアクラスはofパブリックコンストラクターの代わりにファクトリメソッドを使用しています。ofメソッドの外観は好きですが、コンストラクターを使用しない理由はわかりません。私が見つけたドキュメンテーションは、これが事実であることを説明しているだけであり、それがなぜそうであるのかについては本当に入りません。

チュートリアルからの引用:

Date-Time APIのほとんどのクラスは、不変のオブジェクトを作成します。つまり、オブジェクトの作成後は変更できません。不変オブジェクトの値を変更するには、元のオブジェクトの変更されたコピーとして新しいオブジェクトを構築する必要があります。これは、Date-Time APIが定義によりスレッドセーフであることも意味します。これはAPIに影響し、日付または時刻オブジェクトの作成に使用されるほとんどのメソッドには、コンストラクターではなく、of、from、またはwithがプレフィックスとして付けられ、setメソッドはありません。

回答:


9

ファクトリメソッドを使用すると、コンストラクタを簡略化できます。時間のどのインスタンスがオブジェクトによって表されるかの違いは、インスタントの計算から分離できます。

多くのファクトリーメソッドがあり、一部は文字列解析を実行し、一部は他の日付または時刻表現から変換しますが、オブジェクトを作成するに発生する可能性のあるすべてのことです(新しいオブジェクトの作成が必要な場合でも)。キャッシュされたもの)。

もう1つの大きな利点は、ファクトリメソッドにわかりやすい名前を付けることができることLocalTime.parseです。たとえば、文字列表現を解析するためのメソッドには名前が付けられます-これはコンストラクタでは不可能です。


この答えを改善するために、たとえば、ファクトリーメソッドで返すインスタンスを選択するMonthオブジェクトの例を追加できます。
softarn 2016年

8

不変タイプの値を取得するときに、常に新しいオブジェクトを作成する必要はありません。コンストラクター呼び出しが常に新しいオブジェクトを作成する間、ファクトリーメソッドは既に作成されているオブジェクトを返す場合があります。

ファクトリメソッドには他にもいくつかの利点がありますが、それらは不変性と日時APIとはあまり関係がありません。たとえば、ファクトリメソッドには名前があり、サブタイプのオブジェクトを返す場合があります。


5

私はJavaのDate-Time APIを設計または作成しなかったので、「なぜこのようにしたのか」と断言することはできません。しかし、私は彼らがこのようにそれをするべきである2つの主要な理由を提案することができます:

  1. 表現力
  2. 平行形

まず、コンテキストを検討してください。Javaの日付と時刻のコードは、非常にトリッキーな主題を扱う大きくて複雑なパッケージです。「時間」と同様に、この概念の実装には、地理的な場所(タイムゾーン)、言語、カレンダーシステム、時計の解像度、時間スケール、数値表現、データソース、および間隔に応じたさまざまな解釈と形式が含まれます。修正デルタ(うるう年/日など)は一般的で、いつでも不規則に挿入されることがあります(うるう秒)。しかし、パッケージはコア機能であり、広く使用される可能性が高いため、これらすべてのバリエーションを正確かつ正確に処理することが期待されています。それは難しい注文です。その結果、当然のことながら、それぞれに多くのメソッドを持つ多くのクラスを含む複雑なAPIができます。

では、なぜof「通常の」クラスコンストラクタではなくメソッドを使用するのでしょうか。以下のためにたとえば、なぜLocalDate.of(...)LocalDate.ofEpochDay(...)と、LocalDate.ofYearDay(...)というだけの一握りよりもnew LocalDate(...)呼び出し?

まず、表現力:個々の名前付きメソッドは、構築の意図と消費されるデータの形式を表現します。

とりあえず、Javaのメソッドのオーバーロードが、必要なさまざまなコンストラクタを許可するのに十分であると仮定します。(ダックタイピング単一vs動的vs複数ディスパッチについての言語機能の議論に入らないで、私はただ言います:時々これは真であり、時々そうではありません。今のところ、技術的な制限はないと仮定してください。)

コンストラクターのドキュメントに「単一のlong値のみが渡された場合、その値は年ではなくエポック日数として解釈される」と記載される可能性があります。他のコンストラクターに渡される低レベルのデータ型が異なる場合(たとえば、エポックの場合のみが使用されlong、他のすべてのコンストラクターがを使用する場合int)、これで問題が解決する可能性があります。

LocalDate単一longのでコンストラクタを呼び出すコードを見ると、特に年が渡されることは間違いなく最も一般的なケースであり、年が渡されるコードに非常に似ています。その共通のコンストラクタを持つことは可能かもしれませんが、それは必ずしも大きな明快さをもたらすとは限りません。

ofEpochDayただし、APIを明示的に呼び出すと、ユニットと意図の明確な違いがわかります。同様にLocalDate.ofYearDay、共通monthパラメーターがスキップされていること、および日パラメーターはまだであるがint、ではdayOfYearないことを示しdayOfMonthます。明示的なコンストラクターメソッドは、何が起こっているかをより明確に表現することを目的としています。

PythonやJavaScriptなどの動的およびダック型の言語には、代替解釈を通知する他の手段(オプションのパラメーターや帯域外センチネル値など)がありますが、PythonおよびJS開発者でさえ、異なる解釈を示すために区別されたメソッド名を使用することがよくあります。技術的な制約(静的に型付けされた単一ディスパッチ言語)とその文化的慣習/イディオムの両方を考えると、Javaではさらに一般的です。

第二に、並列形式LocalDate2つのofメソッドに加えて、またはその代わりに、標準のJavaコンストラクターを提供できます。しかし、何のために?それはまだ必要ofEpochDayofDayYearそれらのユースケースのために。一部のクラスは、コンストラクターと同等のofXYZメソッドの両方を提供します-たとえば、両方Float('3.14')Float.parseFloat('3.14')存在します。しかしofXYZ、何かにコンストラクタが必要な場合は、常にそれらを使用しないのはなぜですか?それはより単純で、より規則的で、より並列です。不変タイプとして、大部分LocalDate方法は、コンストラクタです。ある方法の七大グループコンストラクトは新しいインスタンス(つまりは、それぞれatfromminusofparseplus、およびwithプレフィックス)。LocalDateの60以上のメソッドのうち、35以上がデファクトコンストラクタです。これらのうち2つだけを簡単に標準のクラスベースのコンストラクタに変換できます。他のすべてと平行ではない方法でこれらの2を特殊ケース化することは本当に意味がありますか?これら2つの特殊なケースのコンストラクターを使用するクライアントコードは、特に明確または単純になりますか?おそらく違います。それは、クライアントコードをより多様にし、簡単ではなくなるかもしれません。

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