38

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);
}
}

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

4

8 に答える 8

59

javadocsから:

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

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

たとえばArrayList<Integer>、配列の代わりにor配列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最初に正しいサイズの配列を渡すことで、メソッド内で新しい配列を割り当てるためにリフレクションを使用する必要がなくなりました。これはもはや当てはまらず、両方のバージョンを同じ意味で使用できます。

于 2013-03-14T23:44:13.970 に答える
9

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

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

配列をキャストせずに戻ってきます。

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

@SuppressWarnings("unchecked")

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

于 2013-03-14T23:37:01.947 に答える
4

メソッドにこの署名がある理由は、toArrayAPI がジェネリックより前にあるためです。

 public Object[] toArray(Object[] a)

Java 1.2 で導入されました。

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

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)ます。ただし、この方法で署名を変更すると、下位互換性がなくなります。

于 2013-03-14T23:39:31.697 に答える
3

これタイプ セーフClassCastExceptionです。それが一般的にタイプセーフの意味です。

ArrayStoreException異なります。「型安全ではない」に含めると、JavaArrayStoreExceptionのすべての配列は型安全ではなくなります。

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

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

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

を避けることはできないので、可能な限り一般的なものにしないArrayStoreExceptionのはなぜですか? すべての要素がその型のインスタンスになることが何らかの形でわかっている場合、ユーザーが無関係な型の配列を使用できるようにするには?

于 2013-03-15T03:44:37.180 に答える
0

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

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

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

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

(編集: いいえ、これは の適切な使用例ではありません。httpssuper ://stackoverflow.com/a/2800425/2158288を参照してください)

于 2013-03-15T02:09:54.183 に答える