Java.utils
パッケージの Collections.list() メソッドがArrayList<T>
ではなくList<T>
を返すという正当な理由はありますか?
明らかにArrayList
はList
ですが、実装タイプではなくインターフェースタイプを返すことは一般的には良い習慣であるという印象を受けています。
免責事項:私はJDKの作成者ではありません。
自分のコードをインターフェイスに書き込むのは正しいことですが、mutableコレクションを返す場合はサードパーティの場合は、どのようなList
が返ってくるのかをサードパーティに知らせることが重要です。
LinkedList
とArrayList
は、さまざまな操作でパフォーマンスが大きく異なります。たとえば、ArrayList
の最初の要素を削除するとO(n)
になりますが、LinkedList
の最初の要素を削除するとO(1)
になります。
戻り値の型を完全に指定することにより、JDKの作成者は、明確なコードで追加情報を伝えるを使用して、どのような種類のオブジェクトが返されるかを確認できるため、このメソッドを適切に使用するコードを記述できます。 。 LinkedList
が本当に必要な場合は、ここで指定する必要があります。
最後に、実装を介してインターフェイスにコーディングする主な理由は、実装が変更されると考える場合です。 JDKの作成者は、おそらくこのメソッドを決して変更しないと考えています。 LinkedList
またはCollections.UnmodifiableList
を返すことは決してありません。ただし、ほとんどの場合、おそらく次のようにします。
List<T> list = Collections.list(enumeration);
List
を返すときは、プログラムをインターフェイスに昇格させます。これは非常に良い方法です。ただし、このアプローチには制限があります。たとえば、ArrayList
に定義されていて、List
インターフェースに存在しないメソッドを使用することはできません-詳細は this answer を参照してください。
Java™チュートリアルの API Design を引用しています。
...任意のタイプのオブジェクトを返すことは問題ありませんimplementsまたはextendsコレクションインターフェースの1つ。これは、インターフェースの1つにすることも、これらのインターフェースの1つを拡張または実装する特殊用途タイプにすることもできます。
..ある意味では、戻り値は入力パラメーターの反対の振る舞いを持つべきです:それはbestであり、一般。たとえば、常に
SortedMap
を返すことが確実な場合は、関連するメソッドにSortedMap
ではなくMap
の戻り値の型を指定する必要があります。SortedMap
インスタンスは、通常のMap
インスタンスよりもビルドに時間がかかり、より強力です。モジュールがすでにSortedMap
を構築するために時間を費やしていることを考えると、ユーザーにその増加したパワーへのアクセスを与えることは理にかなっています。さらに、ユーザーは、返されたオブジェクトを、SortedMap
を要求するメソッドだけでなく、Map
を受け入れるメソッドにも渡すことができます。
ArrayList
は基本的に配列であるため、「コレクション配列」が必要な場合は、これらが最初の選択肢です。したがって、列挙型をリストに変換する場合、私の選択は配列リストになります。
それ以外の場合でも、次のように記述しても有効です。
List<T> list = Collections.list(e);
新しく作成された可変オブジェクトの「排他的所有権」を返す関数は、多くの場合、最も具体的なタイプである必要があります。不変オブジェクトを返すオブジェクトは、特にそれらが共有される可能性がある場合は、それほど限定的でないタイプを返す必要があります。
区別の理由は、前者の場合、オブジェクトは常に指定されたタイプの新しいオブジェクトを生成できることであり、受信者はオブジェクトを所有し、受信者が実行したいアクションがわからないためです。通常、オブジェクトを返すコードが、代替のインターフェース実装が受信者のニーズを満たすことができるかどうかを知る方法はありません。
後者の場合、オブジェクトが不変であるという事実は、関数が、より複雑な型が実行できるすべてのことを実行できる代替型を識別できることを意味しますその正確な内容を考慮して。たとえば、Immutable2dMatrix
インターフェースは、ImmutableArrayBacked2dMatrix
クラスとImmutableDiagonal2dMatrix
クラスによって実装される場合があります。正方形Immutable2dMatrix
を返すことになっている関数は、主対角線からのすべての要素が偶然ゼロの場合はImmutableDiagonalMatrix
インスタンスを返し、そうでない場合はImmutableArrayBackedMatrix
を返すことを決定できます。前者のタイプは、はるかに少ないストレージ容量を必要としますが、受信者はそれらの違いを気にする必要はありません。
具体的なImmutableArrayBackedMatrix
ではなくImmutable2dMatrix
を返すと、コードは配列に含まれる内容に基づいて戻り値の型を選択できます。また、配列を返すことになっているコードがImmutable2dMatrix
の適切な実装を保持している場合、新しいインスタンスを構築する必要がなく、単にそれを返すことができます。不変オブジェクトを操作する場合、これらの要素はどちらも大きな「利点」になります。
ただし、可変オブジェクトを操作する場合は、どちらの要素も関係しません。可変配列が生成されたときに主対角線から外れた要素がない可能性があるという事実は、そのような要素が決してないことを意味しません。したがって、ImmutableDiagonalMatrix
は事実上Immutable2dMatrix
のサブタイプですが、MutableDiagonalMatrix
はMutable2dMatrix
のサブタイプではありません。後者はメインからのストアを受け入れることができるためです。前者はできませんが対角線。さらに、不変オブジェクトは共有できることが多く、共有する必要がありますが、可変オブジェクトは一般に共有できません。特定のコンテンツで初期化された新しい可変コレクションを要求される関数は、バッキングストアが要求されているタイプと一致するかどうかに関係なく、新しいコレクションを作成する必要があります。
オブジェクトで直接メソッドを呼び出すのではなく、インターフェイスのメソッドを呼び出すときにsmallオーバーヘッドがあります。
このオーバーヘッドは、多くの場合、1つまたは2つのプロセッサ命令にすぎません。メソッドがfinalであることをJITが認識している場合、メソッドを呼び出すオーバーヘッドはさらに低くなります。これはほとんどのコードでは測定できませんが、Java.utilsの低レベルのメソッドでは、問題のある一部のコードで使用される可能性があります。
また、他の回答で指摘されているように、返されるオブジェクトの具体的なタイプは(インターフェースの背後に隠されている場合でも)、それを使用するコードのパフォーマンスに影響します。このパフォーマンスの変化は非常に大きくなる可能性があるため、呼び出し側のソフトウェアが機能しなくなる可能性があります。
明らかにJava.utils
の作成者は、Collections.list()を呼び出すすべてのソフトウェアがその結果に対して何を行うかを知る方法がなく、Collections.list()の埋め込みを変更した場合にこのソフトウェアを再テストする方法はありません。 そのため、型システムで許可されている場合でも、Collections.list()の埋め込みを変更して別のタイプのリストを返すことはありません!
独自のソフトウェアを作成するときは、(うまくいけば)すべてのコードをカバーする自動テストがあり、コードがどのように相互に関連しているかをよく理解していると、パフォーマンスの問題がどこにあるかがわかります。ソフトウェアの設計が変更されている間、呼び出し元を変更せずにメソッドを変更できることは大きな価値があります。
したがって、2セットのトレードオフは大きく異なります。