web-dev-qa-db-ja.com

「List <E>」が「インターフェース」であるのに「抽象クラス」ではないのはなぜですか?

階層を定義する中で、firstly、派生する具体的なクラスがそれを持っているという理由だけで、_abstract class_に抽象メソッド(動作)を埋め込むと考えることができます特定の実装によるコア動作としての動作、2番目に、派生メソッドのみがinterfaceに埋め込まれると考えることができます具象クラスは、特定の実装を持つ非コア動作(ペリフェラル)としてその動作を所有します。

私はこれに依存しない 上記のポイントをサポートします。以下は2つの参照です。


1)この点をサポートする最初の参照:

インターフェイスはミックスインを定義するのに理想的です。大まかに言えば、mixinはクラスが実装できるタイプです「基本タイプ」に加えて、いくつかのオプションの動作を提供することを宣言します。たとえば、Comparableは、クラスがそのインスタンスが他の相互に比較可能なオブジェクトに関して順序付けられていることを宣言できるようにする混合インターフェイスです。このようなインターフェースは、オプション機能をタイプの主要機能に「混合」できるため、ミックスインと呼ばれます。抽象クラスは、既存のクラスに組み込むことができないのと同じ理由で、ミックスインを定義するために訴えることはできません。クラスは複数の親を持つことができず、クラス階層にミックスインを挿入するための合理的な場所がありません。

2)この点をサポートする2番目の参照

UMLスケッチでは、interfaceモデルと具象classモデルの関係はRealisationという名前で、_abstract class_モデルと具象classモデルの名前は_is-a_リレーションです。ここで_is-a_は、具象classrealisationリレーションとは異なりコアの動作を持っている場合に通知できます。

したがって、これらの2つの参照では、_list<E>_および_Collection<E>_は_abstract class_であるように見えますが、interfaceではありません。


上記の説明で、私は理解したいと思います、

_Collection<E>_および_List<E>_が_Java.util_パッケージでinterfaceとして設計されているのはなぜですか?

私の理解は正しいですか?

3
overexchange

これらの抽象化をインターフェースとして実装すると、柔軟性が高まります。

インターフェースを使用すると、プログラマはJavaで型の多重継承を使用できます。このようにして、継承階層に関係なく、任意のクラスをインターフェースのインスタンスとして扱うことができます。

単一のクラスに任意の数のインターフェースを実装できますが、(extendsキーワードで表されるように)単一のスーパークラスしか持つことができません。

同時に、特定のinterfaceのスケルトン実装を提供することを妨げるものは何もありません。 abstract class実装し、自由に使用してください。共通のパーツを配置する場所は1つしかなく、APIのクライアントを実際の実装に依存させることはありません。

さらに、リストには非常に異なる実装があり、コア動作の詳細はまったく類似していないメカニズムに依存する場合があります。

たとえば、LinkedListArrayListを見てください。

最初のものは、相互リンクされた多数のオブジェクトによって支えられています。後者は配列に要素を格納します。これらのデータ構造の要素にアクセスする方法は、単に異なります。これらの操作の効果は同じです(当然のことながら、これらのコレクションはどちらもListインターフェイスを実装しています)。実際の動作は、これらの操作を実行するために使用されるアルゴリズムはあまり一般的ではありません。

偶然にも、これらの具象クラスには、スケルトン実装として機能するabstractスーパークラスもあります。 LinkedListListおよび AbstractSequentialList のインスタンスですが、ArrayListListおよび AbstractList のインスタンスです

Listは、その汎用性のためにインターフェースです。実装については何も想定していません。一方、AbstractSequentialListAbstractListは、要素へのアクセスの特定の方法を想定しています。したがって、それらは抽象クラスとして実装されます。

質問の始まりに応えて

階層を定義している間、抽象メソッド(動作)を抽象クラスに埋め込むと考えることができるのは、具体的なクラスを派生させると、その特定の実装のコア動作として、抽象メソッド(動作)をインターフェースに埋め込むと考えることができるからです。派生した具象クラスは、特定の実装を持つコア動作として(ただしペリフェラルとして)持つことはありません。

上記の定義は、この でよく理解できます。

優れたリファレンスの1つでもこの点がサポートされています。

Joshua Bloch - Effective Java

単一のクラスに複数のインターフェースを実装することで、関係のない可能性のある一連の動作を組み合わせたり、peripheral behaviourと表現したりできますが、これはそのインターフェースの目的ではなく単なる使用例です全体。

インターフェースを使用してミックスインを作成することは、それらの優れた使用例であり、Javaで多重継承を行う唯一の方法ですが、このコンテキストの外でインターフェースを自由に使用できます。

インターフェイスを使用して、クラスファミリのコア動作のセットを定義することは完全に有効です。この場合、ミックスインとして使用されているとは言えません。それは契約を定義するだけです。同時に、プログラマが望めば、他のクラスでミックスインとして使用できます。これは、これらの概念を使用する名前とコンテキストの問題です。

coreperipheralの動作の違いは、クラスとインターフェースの違いとはまったく異なります。ここで本当に違いがあるのは、実装されたインターフェースのセットがwhatを指定していることです。継承階層のクラス(抽象かそうでないか)がhowこれらを定義しています。物事が行われることです。

すべてのメソッドが抽象として宣言されている抽象クラスは、型の多重継承に関する機能の欠如により使用が大幅に制限された、実装が不十分なインターフェースのようなものです。

13
toniedzwiedz

したがって、インターフェイスは契約を意味します。インターフェイスを実装するクラスには、これらのパラメーター型を持つこれらのメソッドが含まれ、この型が返されることが保証されます。これは、同じことを行うための多くの異なる方法(リストの維持、並べ替えなど)があることを知っている場合に最適です。

一方、抽象クラスは、これを実装する方法はいくつかあると述べていますただし、これらはすべて、いくつかの機能セットを共有します。この機能は、このクラスで提供します。ここで2つが分岐します。 1つは、すべての実装がまったく同じ方法で1つの部分を実行することを知っているいくつかの知識を持っていると言います。これは多くの場合に当てはまる可能性がありますが、リストや並べ替えなどの場合、ほとんどの場合、まだ考えていないことを実行するための新しい斬新な方法がいくつかあります。そのため、抽象クラスではなくインターフェイスを使用します。

5
Ampt

参照(Joshua Bloch、Effective Java)を文脈から引用している。これは、インターフェースの特定の目的を述べているわけではなく、その目的でのみ使用する必要があることを示しています。それはそれらのための可能なアプリケーションの(非網羅的な)リストを列挙しており、それらすべては一般的な見出し「抽象クラスよりインターフェースを好む」に分類されます。これからどのように決定するのかList<>は抽象クラスとしてはより良い方法だと思いますが、よくわかりませんが、全体的な目的を理解せずにブロッホの議論の詳細を理解しているようです。

2
Jules

abstract classの代わりにinterfaceを使用するコレクション型は誤りです。実際、Java SE 8言語には奇妙な拡張機能が組み込まれているため、インターフェースが実装メソッドを持つことができるほどの大きな間違いです。

なぜ間違いなのか?理想的には、実装に依存せずにインターフェース(Javaキーワードではなく「インターフェース」の一般的な定義)を定義する必要があります。 Java 2コレクションフレームワークは、型に必要なすべてのメソッドを事前に決定できるという間違いなく傲慢な仮定を使用して開発されました。この姿勢により、Javaのほとんどすべての主要なリビジョンでコレクションをサポートするハックが発生しています。

Listinterfaceであるという奇妙な利点があります。 AbstractCollectionは、ListSetの両方の一般的な実装に使用されますが、それらをサブタイプ化する必要はありません。しかし、たいていの場合、そのようなサブタイプは不適切です。