新しいJava.timeパッケージでは、コアクラスはパブリックコンストラクターの代わりにファクトリメソッドof
を使用しています。 of
メソッドの外観が好きなのに、コンストラクターを使用しない正当な理由がわかりません。私が見つけたドキュメンテーションは、これが事実であることを説明しているだけであり、それがなぜそうであるのかについては本当に触れていません。
チュートリアル からの引用:
Date-Time APIのほとんどのクラスは、不変のオブジェクトを作成します。つまり、オブジェクトの作成後は変更できません。不変オブジェクトの値を変更するには、元のオブジェクトの変更されたコピーとして新しいオブジェクトを構築する必要があります。これは、Date-Time APIが、定義上、スレッドセーフであることも意味します。これはAPIに影響し、日付または時刻オブジェクトの作成に使用されるほとんどのメソッドには、コンストラクターではなく、of、from、またはwithがプレフィックスとして付けられ、setメソッドはありません。
ファクトリメソッドを使用すると、コンストラクタを簡略化できます。オブジェクトによって表される時間whichのインスタンスは、インスタントの計算から分離できます。
多くのファクトリメソッドがあり、一部は文字列解析を実行し、一部は他の日付または時刻表現から変換しますが、すべてのことが起こりますbeforeオブジェクトは作成されます(新しいオブジェクトの作成が必要な場合でも、キャッシュされたオブジェクトである場合もあります)。
もう1つの大きな利点は、ファクトリメソッドが説明的な名前を持つことができるということです。文字列表現を解析するためのメソッドには、 LocalTime.parse
-コンストラクタでは不可能です。
不変タイプの値を取得するときに、常に新しいオブジェクトを作成する必要はありません。コンストラクター呼び出しが常に新しいオブジェクトを作成する一方で、ファクトリーメソッドは既に作成されているオブジェクトを返す場合があります。
ファクトリメソッドには他にもいくつかの利点がありますが、それらは不変性と日時APIとはあまり関係がありません。たとえば、ファクトリメソッドには名前があり、いくつかのサブタイプのオブジェクトを返す場合があります。
私はJavaのDate-Time APIを設計または作成しなかったので、「なぜこのようにしたのか」と断言することはできません。ただし、このようにすべき2つの主な理由を提案できます。
まず、コンテキストを検討してください。Javaの日付と時刻のコードは、veryトリッキーな主題を扱う大きくて複雑なパッケージです。 「時間」と同じように、概念の実装には、地理的な場所(タイムゾーン)、言語、カレンダーシステム、時計の解像度、時間スケール、数値表現、データソース、および間隔に応じたさまざまな解釈と形式が含まれます。修正デルタ(うるう年/日など)は一般的で、いつでも不規則に挿入されることがあります(うるう秒)。しかし、パッケージはコア機能であり、広く使用される可能性が高いため、これらすべてのバリエーションを正確かつ正確に処理することが期待されています。それは難しい注文です。その結果、当然のことながら、それぞれに多くのメソッドを持つ多くのクラスを含む複雑なAPIができます。
では、なぜ「通常の」クラスコンストラクタではなくof
メソッドを使用するのでしょうか。 example の場合、ほんの一握りのLocalDate.of(...)
呼び出しではなく、なぜLocalDate.ofEpochDay(...)
、LocalDate.ofYearDay(...)
、およびnew LocalDate(...)
ですか?
まず、表現力:個々の名前付きメソッドは、コンストラクトのインテントと消費されるデータのフォームを表現します。
ここで、Javaの メソッドのオーバーロード で、必要なさまざまなコンストラクタを許可できると仮定します。 ( ダックタイピング と 単一vs.動的vs.複数ディスパッチ についての言語機能の議論に入らないで、私はただ言う:これは正しい場合もあればそうでない場合もあります。今のところ、技術的な制限はないと想定してください。)
コンストラクターのドキュメントに「単一のlong
値のみが渡された場合、その値は年ではなくエポックの日数として解釈される」と記載される可能性があります。他のコンストラクターに渡される低レベルのデータ型が異なる場合(たとえば、エポックの場合のみlong
を使用し、他のすべてのコンストラクターはint
を使用する)、これでうまくいく場合があります。
LocalDate
コンストラクターを1つのlong
で呼び出すコードを見てみると、年が渡されるコードに非常によく似ています。特に、おそらく最も一般的なケースでは、年が渡されます。 。その共通のコンストラクタを持つことは可能かもしれませんが、それは必ずしも大きな明快さをもたらすとは限りません。
ただし、APIが明示的にofEpochDay
を呼び出すと、単位と意図の明確な違いがわかります。同様に、_LocalDate.ofYearDay
_は、共通のmonth
パラメータがスキップされていること、およびint
であるにもかかわらず、dayパラメータがdayOfYear
ではなくdayOfMonth
。明示的なコンストラクターメソッドは、何が起こっているかをより明確に表現することを目的としています。
PythonやJavaScriptなどの動的およびダック型の言語には、別の解釈を示す他の手段があります(たとえば、オプションのパラメーターや帯域外 センチネル値 )ですが、PythonおよびJS開発者は、異なる解釈を示すために区別されたメソッド名を使用することがよくあります。両方の技術的制約(静的に型付けされた単一ディスパッチ言語です)を考えると、Javaではさらに一般的です)およびその文化的慣習/イディオム。
次に、並列形式です。 LocalDate
は、2つのof
メソッドに加えて、またはその代わりに、標準のJavaコンストラクタを提供できます。しかし、何のためにofEpochDay
とofDayYear
は、これらの使用例に対応しています。一部のクラスは、コンストラクターと、同等のofXYZ
メソッドを提供しています-たとえば、Float('3.14')
とFloat.parseFloat('3.14')
が存在します。ただし、ofXYZ
コンストラクタが必要な場合は、常にそれらを使用しないのはなぜですか?これは、より単純で、より規則的で、より並列です。不変の型として、大多数のLocalDate
メソッドはコンストラクターです。 新しいインスタンスを構築する[////] 7つの主要なメソッドグループ があります(それぞれ、at
、from
、 minus
、of
、parse
、plus
、およびwith
プレフィックス)LocalDate
の60以上のメソッドのうち、35 +はデファクトコンストラクターです。標準のクラスベースのコンストラクターに簡単に変換できるのは2つだけです。他のすべてと並列ではない方法でこれらの2つを特殊なケースにしても意味がありますか?クライアントコードはそれら二つの特別なケースコンストラクタは特に明確または単純ですか?おそらく違います。それはクライアントコードをより多様化し、簡単ではなくなるかもしれません。