JavaのCollectionインターフェースを使用する正当な理由はありますか?


11

そのインターフェイスの特定の実装に縛られないように、利用可能な最も一般的なインターフェイスを使用すべきだという議論を聞いたことがあります。このロジックはjava.util.Collectionなどのインターフェースに適用されますか?

私はむしろ次のようなものを見たいです:

List<Foo> getFoos()

または

Set<Foo> getFoos()

の代わりに

Collection<Foo> getFoos()

最後のケースでは、どの種類のデータセットを扱っているのかわかりませんが、最初の2つのインスタンスでは、順序付けと一意性についていくつかの仮定を立てることができます。DOES のjava.util.Collectionはセットとリストの両方のための論理的な親であることの有用性の外側を持っていますか?

コードレビューを行うときにCollectionを使用するコードに出くわした場合、その使用法が正当であるかどうかをどのように判断し、より具体的なインターフェイスに置き換えるためにどのような提案をしますか?


3
java.security.certで、戻り値の型が1つであることに気付きましたCollection<List<?>>か?コーディングの恐怖について話しましょう!
マクニール

1
@Macneilあなたがどのクラスを参照しているのかわかりませんが、そのような戻り値の型は確かに非常に賢明です。それは本質的にあなたが持っていることを示していますコレクション(すなわち、物事の束含まれていないの賢明な順序)を示します(つまり、物事含む持つの賢明な順序を)オブジェクトを(その種類我々は静的に知っていない、すなわちアイテムをなんらかの理由)。理不尽に思えない。
Zero3

回答:


13

抽象化は実装よりも長生きします

一般に、デザインが抽象的であればあるほど、有用なままである可​​能性が長くなります。そのため、コレクションはサブインターフェースよりも抽象的であるため、コレクションに基づくAPIデザインは、リストに基づくAPIデザインよりも有用である可能性が高くなります。

ただし、最も重要な原則は、最も適切な抽象化を使用することです。したがって、コレクションが順序付けされた要素をサポートする必要がある場合はリストを義務付け、重複がない場合はセットを義務付ける、などです。

汎用インターフェース設計に関する注意

Collectionインターフェースをジェネリックと一緒に使用することに興味があるので、以下が役に立つかもしれません。Joshua Blochによる効果的なJavaは、ジェネリックに依存するインターフェイスを設計する際に、次のアプローチを推奨しています:Producers Extend、Consumers Super

これはPECSルールとも呼ばれます。基本的に、データを生成するジェネリックコレクションがクラスに渡される場合、署名は次のようになります。

public void pushAll(Collection<? extends E> producerCollection) {}

したがって、入力タイプはEまたはEの任意のサブクラスになります(Java言語では、Eはそれ自体のスーパークラスとサブクラスの両方として定義されます)。

逆に、データを消費するために渡される汎用コレクションには、次のような署名が必要です。

public void popAll(Collection<? super E> consumerCollection) {}

この方法は、正しく、あなたが渡すことができるようになりますので、あなたのユーザーにあなたのインターフェースが少なく意外になります。このアプローチを使用して、E.全体の任意のスーパークラスに対処しますCollection<Number>Collection<Integer>、それらを正しく処理する必要があります。


6

Collectionインターフェース、そして最も許容フォームはCollection<?>、のために素晴らしいですパラメータあなたが受け入れること。Javaライブラリ自体での使用に基づいて、戻り値の型よりもパラメータ型としてより一般的です。

戻り型については、あなたのポイントは有効だと思います。人々がそれにアクセスすることが期待される場合、実行される操作の順序(Big-Oの意味で)を知っている必要があります。Collection返されたコレクションを反復処理して別のコレクションに追加しますcontainsが、O(1)、O(log n)、またはO(n)のいずれの操作であるかわからないので、それを呼び出すのは少しおかしく思えます。もちろん、持っているからといって、Setそれがハッシュセットまたはソートされたセットであることを意味するわけではありませんが、ある時点で、インターフェースが合理的に実装されていると仮定します(そして、仮定があれば計画Bに行く必要があります)間違っていることが示されています)。

トムが言及しているように、Collectionカプセル化を維持するためにaを返す必要がある場合があります。より具体的な何かを返すことができたとしても、実装の詳細が漏洩することは望ましくありません。または、トムが言及した場合、より具体的なコンテナを返すことができますが、それを構築する必要があります。


2
2番目の点は少し弱いと思います。コレクションがリストであるかどうかに関係なく、コレクションがどのように実行されるかはわかりません-それらは単なる抽象化のためです。具体的な最終クラスがない限り、実際にはわかりません。
マークH

また、何かがコレクションであることしかわからない場合は、重複が含まれているかどうかはわかりません。それを振り返って、コレクションを返すことが適切な場合は、重複を含まず、重要な順序を持たないコレクション(自然にSetになる)がありますが、何らかの理由で戻りメソッドの実装リストを使用します。リストを返すことは重要な順序を意味するため、返したくありませんが、セットを作成するという厳しい手続きを経ずにセットを返すことはできません。したがって、コレクションを返します。
トムアンダーソン

@トム:良い点!
マクニール

5

私は完全に反対の観点からそれを見て、尋ねます:

コードレビューを行うときにList <>を使用するコードに出くわした場合、その使用が正当化されるかどうかをどのように判断しますか?

これを正当化するのは非常に簡単です。コレクションが提供しない機能が必要な場合は、リストを使用します。余分な機能が必要ない場合は、どのような理由がありますか?(そして、「見たい」とは思わない)

コレクションを読み取り専用の目的で使用し、一度にデータを入力し、完全に繰り返し処理する場合がたくさんあります-手動でインデックスを作成する必要がありますか?

実際の例を挙げます。データベースに対して簡単なクエリを実行するとします。(SELECT id,name,rep FROM people WHERE name LIKE '%squared')関連データを取得し、Personオブジェクトにデータを入力して、PemListにemを入れます)

  • インデックスでアクセスする必要がありますか?-無意味になります。インデックスとIDの間にマッピングはありません。
  • インデックスに挿入する必要がありますか?-いいえ、追加する場合、DBMSはどこに配置するかを決定します。

それでは、これらの追加のメソッドに対してどのような正当性がありますか?(とにかく私のPersonListには実装されないままになります)


公正なポイント。私の質問は、コードレビューを行っている間、コレクションを返すDAOを見続けている特定のインスタンスに関するものだと思いますが、これらのDAO呼び出しは常にエンティティのセットを返すことを知っています。私の主張は、これらの場合、戻り値の型が一意性を示すことであり、この情報はそのメソッドを使用する必要がある人に役立ちます(たとえば、重複をチェックする必要はありません)。
フィル

DBを照会した場合、2つの結果オブジェクトをequals()で比較しても、trueは生成されません。したがって、複製のためにオブジェクトを比較する別の方法が必要になります(たとえば、同じ名前、同じID、どちらも?)。重複自体を削除する場合は、DAOにそれらを比較する方法を伝える必要があります-ただし、重複が存在するかどうかを判断しているのはユーザーであるため、呼び出し元のコードのコレクションを使用する方が簡単です。(より多くの抽象化層を回避して、DAOに惑星で可能なあらゆる同等性チェックを実行する方法を通知するため。)
マークH

同意しましたが、Hibernateを使用しており、エンティティがequals()を実装していることを確認します。そのため、DAOがエンティティを返している場合、新しいHashSet()。addAll(results)を非常にすばやく実行し、呼び出されたメソッドに返すことができます。
フィル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.