web-dev-qa-db-ja.com

Javaの汎用配列

OK、ウェブをグーグルで検索してきましたが、問題の解決策が見つからないようです。たくさんの解決策を見つけましたが、適切な解決策は見つかりませんでした。

ジェネリックの配列を作成する必要があります。ただし、ジェネリック型自体はComparableを拡張します。私が次を試してみると:

public class Hash<T extends Comparable<String>> {
    private T[] hashTable;
    private int tableSize;

    Hash(int records, double load) {
        tableSize = (int)(records / loadFactor);
        tableSize = findNextPrime(tableSize);
        hashTable = (T[])(new Object[tableSize]);  //Error: Ljava.lang.Object; cannot be cast to [Ljava.lang.Comparable;
    }
}

問題は、オブジェクトをComparableを拡張するジェネリックとしてキャストできないことです。これを回避する方法はありますか?

44
user131441

基本的に、ジェネリックと配列は混在しません。簡単な答えは、この問題を回避できるということです。より長い答えは、おそらくそうすべきではないということであり、その理由を説明します。

次のように Array.newInstance() を使用できます。

private Comparable[] hashtable;

...

hashtable = (Comparable[])Array.newInstance(Comparable.class, tableSize);

ただし、パラメータ化された型の配列を作成することはできません。

配列はcovariantです。つまり、実行時に要素のタイプを保持します。 Javaのジェネリックはそうではありません。彼らはtype erasureを使用して、進行中の暗黙的なキャストを基本的にマスクします。それを理解することは重要です。

したがって、Object配列を作成するとき、Comparable配列(またはその他の型)にキャストすることはできません。これは正しくないためです。

例を挙げましょう。ジェネリックでは、これは完全に合法です:

List<String> list = new ArrayList<String>();
List<Integer> list2 = (List<Integer>)list;
list.add(3);

これができない理由でもあります:

public <T> T newInstance(T t) {
  return new T(); // error!
}

つまり、実行時にTのクラスの知識はありません。これが、上記のコードが次のように書かれている理由です。

public <T> T newInstance(T t, Class<T> clazz) {
  return clazz.newInstance();
}

なぜなら、それらはジェネリック引数のランタイム型ではないからです。ただし、配列の場合:

String arr[] = new String[10];
Integer arr2[] = (Integer[])arr; // error!

この場合(imho)にすべきことは、配列を使用するのではなく、ArrayListを使用することです。正直なところ、ArrayListに対して配列を使用する理由はほとんどなく、ジェネリックはその一例にすぎません。

より優れた完全な説明については、(優れた) Java Generics FAQ を参照してください。

コンポーネントの型が具体的なパラメーター化された型である配列を作成できますか?

いいえ、タイプセーフではないためです。

配列は共変です。つまり、スーパータイプ参照の配列はサブタイプ参照の配列のスーパータイプです。つまり、Object[]String[]のスーパータイプであり、文字列配列はObject[]型の参照変数を介してアクセスできます。

...

78
cletus

ここでの他の答えは、一般的にこれに対するより良いアプローチ(特に、代わりにArrayListを使用することの推奨)を提唱しますが、この特定の場合の簡単な答えはそうすることです:

hashTable = (T[])(new Comparable[tableSize]);

(つまり、Objectではなくraw Comparable型の配列を作成します)

Hashオブジェクト内にこの配列へのすべてのアクセスを適切にカプセル化すると、これは機能するはずですが、(他の答えが説明しているように)自分自身を脆弱なままにする可能性があります。

16
matt

あなたがしようとしているキャスト

_(T[])(new Object[tableSize]);
_

配列内のアイテムはオブジェクトのインスタンスであるため、失敗します。オブジェクトは_Comparable<String>_を拡張しないため、Tが次のように定義されているため、キャスト(T [])は失敗します。

_T extends Comparable<String>
_

この問題を解決するには:

  • _Comparable<String>_を拡張するクラスのインスタンスになるように配列をインスタンス化します
  • hashTableを配列(ジェネリック型ではない)からジェネリックコレクション型に変更します。 List<T> hashTable = new ArrayList<T>(tableSize>)
4
Dónal

ジェネリック型の何かをインスタンス化する必要がある場合、しばしば問題に遭遇します。これを回避する最も簡単な方法は、実際にコンストラクタに格納されるクラスを渡すことです。このようにして、実際の型から構築できます。次のようなものを試してください:

public class Hash<T extends Comparable<String>>
{
  Hash(int records, double load, Class<T> class)
  {
    tableSize = (int)(records / loadFactor);
    tableSize = findNextPrime(tableSize);

    hashTable = Java.lang.reflect.Array.newInstance(class, tableSize);
  }

private T[] hashTable;
private int tableSize;

}
1
Chris Dail

他の人から提案された強制キャストは私には役に立たず、違法なキャストの例外を投げました。

ただし、この暗黙のキャストは正常に機能しました。

Item<K>[] array = new Item[SIZE];

itemは、メンバーを含む定義済みのクラスです。

private K value;

この方法では、タイプKの配列(アイテムに値のみがある場合)またはクラスItemで定義したいジェネリックタイプを取得します。

0
vnportnoy