web-dev-qa-db-ja.com

Javaリスト<T> T [] toArray(T [] a)実装

Listインターフェースで定義されているメソッド<T> T[] toArray(T[] a)を見ているだけで、質問があります。なぜ一般的なのですか?そのため、メソッドは完全なタイプセーフではありません。次のコードフラグメントはコンパイルされますが、ArrayStoreExceptionが発生します。

List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);

String[] stringArray = list.toArray(new String[]{});

ToArrayがジェネリックではなく、List型のパラメーターを取得した場合、より良いでしょう。

私はおもちゃの例を書きましたが、それは一般的なものです:

package test;

import Java.util.Arrays;

public class TestGenerics<E> {
    private Object[] elementData = new Object[10];
private int size = 0;

    public void add(E e) {
    elementData[size++] = e;
}

@SuppressWarnings("unchecked")
    //I took this code from ArrayList but it is not generic
public E[] toArray(E[] a) {
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (E[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

    public static void main(String[] args) {

    TestGenerics<Integer> list = new TestGenerics<Integer>();
    list.add(1);
    list.add(2);
    list.add(3);
    //You don't have to do any casting
    Integer[] n = new Integer[10];
    n = list.toArray(n);
}
}

それがそのように宣言されている理由はありますか?

32
midas

javadocs から:

ToArray()メソッドと同様に、このメソッドは配列ベースのAPIとコレクションベースのAPIの間のブリッジとして機能します。さらに、この方法により、出力配列のランタイムタイプを正確に制御でき、特定の状況では、割り当てコストを節約するために使用できます。

これは、プログラマーが配列のタイプを制御できることを意味します。

たとえば、ArrayList<Integer>の代わりにInteger[]必要な配列Number[]またはObject[]配列。

さらに、メソッドは渡された配列もチェックします。すべての要素に十分なスペースがある配列を渡すと、toArrayメソッドはその配列を再利用します。これの意味は:

Integer[] myArray = new Integer[myList.size()];
myList.toArray(myArray);

または

Integer[] myArray = myList.toArray(new Integer[myList.size()]);

と同じ効果があります

Integer[] myArray = myList.toArray(new Integer[0]);

古いバージョンのJavaでは、後者の操作はリフレクションを使用して配列型をチェックし、適切な型の配列を動的に構築します。最初に正しいサイズの配列を渡すことにより、 toArrayメソッド内で新しい配列を割り当てるために使用する必要はありませんでしたが、もはやそうではなく、両方のバージョンを同じように使用できます。

54
beny23

次のようなコードを記述できるように、一般的に宣言されています

Integer[] intArray = list.toArray(new Integer[0]);

戻ってくる配列をキャストせずに。

次の注釈で宣言されます。

@SuppressWarnings("unchecked")

つまり、Javaは信頼しているyoは同じ型の配列パラメーターを渡すため、エラーは発生しません。

8
rgettman

メソッドにこのシグネチャがある理由は、toArray AP​​Iがジェネリックよりも前であるためです:メソッド

_ public Object[] toArray(Object[] a)
_

Java 1.2。

ObjectTに置き換える対応するジェネリックは、100%後方互換性のあるオプションとして導入されました。

_public <T> T[] toArray(T[] a)
_

シグネチャをジェネリックに変更すると、発信者はキャストを回避できます:Java 5の前に、これを行うには発信者が必要です。

_String[] arr = (String[])stringList.toArray(new String[stringList.size()]);
_

今、彼らはキャストなしで同じ呼び出しを行うことができます:

_String[] arr = stringList.toArray(new String[stringList.size()]);
_

編集:

toArrayメソッドのより「モダンな」シグネチャは、オーバーロードのペアです。

_public <T> T[] toArray(Class<T> elementType)
public <T> T[] toArray(Class<T> elementType, int count)
_

これにより、現在のメソッドシグネチャに代わる、より表現力があり、同様に多目的な代替手段が提供されます。 Array.newInstance(Class<T>,int) メソッドを使用して、これを効率的に実装することもできます。ただし、この方法で署名を変更しても、後方互換性はありません。

4
dasblinkenlight

それはis type-safe-それはClassCastExceptionを引き起こさない。それが一般的にタイプセーフの意味です。

ArrayStoreExceptionは異なります。 「タイプセーフではない」にArrayStoreExceptionを含めると、すべてのarrays in Javaはタイプセーフではありません。

投稿したコードもArrayStoreExceptionを生成します。ちょうど試して:

TestGenerics<Object> list = new TestGenerics<Object>();
list.add(1);
String[] n = new String[10];
list.toArray(n); // ArrayStoreException

実際、ユーザーが取得したい型の配列を渡すことを許可することは、単に不可能であり、同時にArrayStoreExceptionを持っていません。何らかのタイプの配列を受け入れるメソッドシグネチャは、サブタイプの配列も許可するためです。

ArrayStoreExceptionを避けることはできないので、理由はありませんできるだけ汎用的にしますか?すべての要素がその型のインスタンスになることを何らかの方法で知っている場合、ユーザーは何らかの無関係な型の配列を使用できますか?

3
newacct

dasblinkenlightはおそらく、これが既存のメソッドの生成と関係があることは正しいと思います。完全な互換性を実現することは微妙です。

beny23のポイントも非常に良いです-メソッドはE[]のスーパータイプを受け入れる必要があります。試みるかもしれない

    <T super E> T[] toArray(T[] a) 

しかしJavaは、ユースケースがないため、型変数でsuperを許可しません:)

(編集:いいえ、これはsuperの良いユースケースではありません。 https://stackoverflow.com/a/2800425/2158288 を参照してください)

0
ZhongYu

この方法がそのままの理由は、ほとんど歴史的です。

ジェネリッククラスと配列型には違いがあります。ジェネリッククラスの型パラメーターは実行時に消去されますが、配列の要素の型は消去されません。そのため、JVMは実行時にList<Integer>List<String>の違いを認識しませんが、Integer[]String[]の違いを認識します。この違いの理由は、Java 1.0以降は常に配列が存在していたのに対し、ジェネリックはJava = 1.5。

Collections APIは、ジェネリックが導入される前にJava 1.2に追加されました。その時点で、Listインターフェースにはすでにメソッドが含まれていました

Object[] toArray(Object[] a);

1.2 JavaDocのこのコピー を参照)。これは、ユーザー指定の実行時タイプで配列を作成する唯一の方法でした。パラメータaは、タイプトークンとして機能します。つまり、返される配列の実行時タイプを決定します(ABのサブクラスであり、A[]B[]のサブタイプと見なされますが、List<A>notList<B>)のサブタイプ。

Java 1.5でジェネリックが導入されたとき、多くの既存のメソッドがジェネリックになり、toArrayメソッドが

<T> T[] toArray(T[] a);

型消去後、元の非汎用メソッドと同じシグネチャを持ちます。

0
Hoopje