web-dev-qa-db-ja.com

多態性:「ArrayList list = new ArrayList」の代わりに「List list = new ArrayList」を使用する理由

可能性のある複製:
なぜJavaクラスのインターフェースを優先すべきですか?

いつ使うべきか

List<Object> list = new ArrayList<Object>();

ArrayListListを継承するため、ArrayListの一部の機能がListにない場合は、ArrayListの一部の機能が失われます。 ?そして、コンパイラはこれらのメソッドにアクセスしようとするとエラーに気付くでしょうか?

136
hqt

これを行う主な理由は、インターフェイスの特定の実装からコードを切り離すことです。このようにコードを書くとき:

List list = new ArrayList();  

コードの残りの部分は、データがList型であることのみを知っています。これは、Listインターフェイスの異なる実装を簡単に切り替えることができるため、望ましいです。

たとえば、かなり大きなサードパーティのライブラリを書いていて、LinkedListを使用してライブラリのコアを実装することにしたとしましょう。ライブラリがこれらのリスト内の要素へのアクセスに大きく依存している場合、最終的には設計上の決定が下手であることがわかります。 ArrayList(O(1)アクセス時間を与える)の代わりにLinkedList(O(n)アクセス時間を与える)を使用する必要があることに気付くでしょう。インターフェイスをプログラミングしていると仮定すると、そのような変更は簡単です。 Listのインスタンスを単に変更するだけで、

List list = new LinkedList();

List list = new ArrayList();  

Listインターフェースによって提供される契約に従うようにコードを記述したため、これが機能することを知っています。

一方、LinkedList list = new LinkedList()を使用してライブラリのコアを実装した場合、コードの残りの部分がメソッドを使用しないという保証がないため、このような変更を行うことは簡単ではありません。 LinkedListクラスに固有です。

全体として、選択は単にデザインの問題です...しかし、この種のデザインは、既存のコードを壊すことなく後で実装固有の変更を行うことができるため、非常に重要です(特に大規模なプロジェクトで作業する場合)。

242
Alex Lockwood

これは、インターフェイスへのプログラミングと呼ばれます。これは、将来Listの他の実装に移行したい場合に役立ちます。 ArrayListにメソッドが必要な場合は、ArrayList a = new ArrayList()である実装にプログラムする必要があります。

67
tsatiz

これは、パブリックインターフェイスを公開するときにも役立ちます。このような方法がある場合、

public ArrayList getList();

次に、変更することにしました。

public LinkedList getList();

ArrayList list = yourClass.getList()を実行していた人は誰でもコードを変更する必要があります。一方、あなたがそうするなら、

public List getList();

実装を変更しても、APIのユーザーにとって何も変わりません。

21
Brendan Long

@tsatizの答えはほとんど正しいと思います(実装ではなくインターフェイスにプログラミングする)。ただし、インターフェイスにプログラミングすることにより、機能が失われることはありません。説明させてください。

変数をList<type> list = new ArrayList<type>として宣言する場合</ code> ArrayListの機能を実際に失うことはありませんany機能。 listArrayListにキャストするだけです。次に例を示します。

List<String> list = new ArrayList<String>();
((ArrayList<String>) list).ensureCapacity(19);

最終的には、tsatizが正しいと思うのは、一度ArrayListにキャストすると、インターフェイスをコーディングしなくなるからです。ただし、最初はインターフェイスにコーディングし、後で必要になった場合は必要に応じて実装にコーディングすることをお勧めします。

お役に立てば幸いです!

10
SecondSun24

これにより、次のような記述が可能になります。

void doSomething() {
    List<String>list = new ArrayList<String>();
    //do something
}

後で、次のように変更できます。

void doSomething() {
    List<String>list = new LinkedList<String>();
    //do something
}

残りのメソッドを変更する必要はありません。

ただし、たとえばCopyOnWriteArrayListを使用する場合は、リストとしてではなく、そのように宣言する必要があります追加のメソッドを使用する場合(addIfAbsentなど) :

void doSomething() {
    CopyOnWriteArrayList<String>list = new CopyOnWriteArrayList<String>();
    //do something, for example:
    list.addIfAbsent("abc");
}
7
assylias

あなたの質問の核心は、program to an interface, not to an implementation

インターフェイスがより抽象化され、変更に対してコードをより柔軟で弾力的にするため、同じインターフェイスの異なる実装を使用できるためです(この場合、List実装をArrayListではなくlinkedListに変更したい場合があります)クライアントを変更することなく。

5
Mouna Cheikhna

問題に複雑さを加えたくないときはいつでもその構造を使用します。これは単なるリストであり、どのようなリストであるかを言う必要はありません。問題には関係ありません。最終的には、ほとんどの場合、残りのソフトウェアのために、ほとんどのソリューションでコレクションを使用します。本当に重要なのは、それが保持するコンテンツであり、新しいオブジェクトをコレクションに追加したくない。

さらに、使用しているリストの実装を変更したい場合に、その構造を使用します。 ArrayListで構築を使用していて、問題がスレッドセーフではなかったとしましょう。ここで、スレッドセーフにし、ソリューションの一部として、たとえばベクターを使用するように変更します。そのリストの他の用途に関しては、それがAraryListであるかベクターであるかは関係なく、リストだけであり、新しい修正は必要ありません。

5
SHiRKiT

一般に、インターフェイスに対してプログラムする必要があります。これにより、いつでも実装を交換できます。これは、知らない実装に合格した場合に特に便利です。

ただし、具体的な実装を使用したい特定の状況があります。たとえば、GWTでシリアル化する場合。

2
stefan bachert