Javaでジェネリック配列を作成する方法は?


1090

Javaジェネリックの実装のため、次のようなコードは使用できません。

public class GenSet<E> {
    private E a[];

    public GenSet() {
        a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation
    }
}

タイプセーフを維持しながらこれをどのように実装できますか?

Javaフォーラムで次のような解決策を見ました。

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

しかし、私は本当に何が起こっているのかわかりません。


14
ここで本当に配列を使用する必要がありますか?コレクションの使用についてはどうですか?
マットb

12
はい、私はまた、コレクションがこの問題に対してよりエレガントであると思います。しかし、これはクラスの割り当てのためのものであり、それらは必須です:(
tatsuhirosatou

3
ここでリフレクトが必要な理由がわかりません。Javaの文法が奇妙です。たとえば、新しいjava.util.HashMap <String、String> [10]は無効です。新しいjava.util.HashMap <long、long>(10)は無効です。new long [] [10]は無効です。newlong [10] []は有効です。そのため、Javaプログラムを作成できるプログラムを作成するのは、見た目よりも難しくなります。
ブロンズ男

回答:


703

私は見返りに質問をしなければなりGenSetません。「チェック済み」か「チェックなし」ですか。どういう意味ですか?

  • チェック済み強い型付けGenSetに含まれているオブジェクトのタイプを明示的に認識している(つまり、コンストラクタがClass<E>引数で明示的に呼び出されたため、メソッドにタイプではない引数が渡されると例外がスローされますE。を参照してくださいCollections.checkedCollection

    ->その場合、次のように記述します。

    public class GenSet<E> {
    
        private E[] a;
    
        public GenSet(Class<E> c, int s) {
            // Use Array native method to create array
            // of a type only known at run time
            @SuppressWarnings("unchecked")
            final E[] a = (E[]) Array.newInstance(c, s);
            this.a = a;
        }
    
        E get(int i) {
            return a[i];
        }
    }
  • オフ弱い型付け。引数として渡されたオブジェクトの型チェックは実際には行われません。

    ->その場合、あなたは書くべきです

    public class GenSet<E> {
    
        private Object[] a;
    
        public GenSet(int s) {
            a = new Object[s];
        }
    
        E get(int i) {
            @SuppressWarnings("unchecked")
            final E e = (E) a[i];
            return e;
        }
    }

    配列のコンポーネントタイプは消去でなければならないことに注意してください typeパラメータのでください。

    public class GenSet<E extends Foo> { // E has an upper bound of Foo
    
        private Foo[] a; // E erases to Foo, so use Foo[]
    
        public GenSet(int s) {
            a = new Foo[s];
        }
    
        ...
    }

これらはすべて、Javaのジェネリックの既知の意図的な弱点に起因します。これは消去を使用して実装されたため、「ジェネリック」クラスは実行時に作成された型引数がわからないため、型を提供できません-明示的なメカニズム(型チェック)が実装されていない限り、安全性。


7
パフォーマンスに関してはどのオプションが最適でしょうか?この配列から要素を頻繁に取得する必要があります(ループ内)。したがって、コレクションはおそらく遅くなりますが、これらの2つの中で最も速いのはどれですか。
user1111929 2012

3
また、ジェネリック型が制限されている場合、バッキング配列は境界型でなければなりません。
モルデチャイ2013

5
@AaronDigulla明確に言うと、それは割り当てではなく、ローカル変数の初期化です。式/ステートメントに注釈を付けることはできません。
kennytm 2013

1
@Varkhanクラス実装内からこれらの配列のサイズを変更する方法はありますか?たとえば、ArrayListのようにオーバーフローした後にサイズを変更したい場合。Object[] EMPTY_ELEMENTDATA = {}ストレージ用に持っているArrayListの実装を調べました。ジェネリックを使用してタイプを知らなくても、このメカニズムを使用してサイズを変更できますか?
JourneyMan 2014

2
(私が探していたものだった)ジェネリック型でメソッドを作りたい人のために、この使用:public void <T> T[] newArray(Class<T> type, int length) { ... }
ダニエル・クビスト

225

あなたはこれを行うことができます:

E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];

これは、Effective Javaでジェネリックコレクションを実装するための推奨方法の1つです。アイテム26。型エラーはなく、配列を繰り返しキャストする必要はありません。 ただし、これは潜在的に危険であるため警告をトリガーし、注意して使用する必要があります。コメントで詳しく説明されているように、これObject[]E[]タイプになりすましており、ClassCastException安全に使用しないと予期しないエラーやsを引き起こす可能性があります。

経験則として、この動作は、キャスト配列が内部で(たとえば、データ構造をサポートするために)使用され、クライアントコードに返されたり公開されたりしない限り安全です。ジェネリック型の配列を他のコードに返す必要がある場合は、リフレクションArrayクラスが適切な方法です。


Listジェネリックを使用している場合は、可能な限り、配列ではなくを使用するほうがはるかに楽しい時間があることを言及する価値があります。確かにときどき選択の余地はありませんが、コレクションフレームワークを使用する方がはるかに堅牢です。


47
String[] s=b;上記のtest()方法のように、配列が任意の種類の型付き配列として扱われる場合、これは機能しません。これは、Eの配列が実際にはObject []でないためです。これはあなたが望むなら重要です、例えばList<String>[]-あなたはそのために使用することはできませんObject[]、あなたはList[]具体的に持っている必要があります。これが、反映されたClass <?>配列の作成を使用する必要がある理由です。
Lawrence Dol、

8
コーナーケース/問題は、あなたがしたい場合は、例えば、あるpublic E[] toArray() { return (E[])internalArray.clone(); }ときinternalArrayのように入力されE[]、実際にそのためですObject[]。これは実行時にタイプキャスト例外で失敗します。これは、Object[]がどんなタイプの配列にも割り当てられないためEです。
Lawrence Dol

17
基本的に、このアプローチは、配列を返さないか、渡さないか、特定の型の配列を必要とするクラス外の場所に格納しない限り機能します。クラスの中にいる限り、Eは消されているので元気です。「危険」なのは、それを返そうとした場合、安全ではないという警告が表示されないためです。しかし、あなたが注意すればそれはうまくいきます。
newacct 2011

3
とても安全です。ではE[] b = (E[])new Object[1];、作成された配列への唯一の参照がでbあり、の型がでbあることがはっきりとわかりますE[]。したがって、異なる型の異なる変数を介して同じ配列に誤ってアクセスする危険はありません。代わりに、あなたが持っていたならObject[] a = new Object[1]; E[]b = (E[])a; 、あなたは自分の使い方に偏執的である必要がありますa
アーロンマクデイド

5
少なくともJava 1.6では、これにより「オブジェクト[]からT []への未チェックのキャスト」という警告が生成されます
Quantum7

61

ここでは、ジェネリックを使用して、型の安全性を維持しながら、探している型の配列を正確に取得する方法を示します(他の回答では、Object配列が返されるか、コンパイル時に警告が表示されます)。

import java.lang.reflect.Array;  

public class GenSet<E> {  
    private E[] a;  

    public GenSet(Class<E[]> clazz, int length) {  
        a = clazz.cast(Array.newInstance(clazz.getComponentType(), length));  
    }  

    public static void main(String[] args) {  
        GenSet<String> foo = new GenSet<String>(String[].class, 1);  
        String[] bar = foo.a;  
        foo.a[0] = "xyzzy";  
        String baz = foo.a[0];  
    }  
}

これは警告なしでコンパイルされmain、でわかるように、GenSetasのインスタンスを宣言するすべての型について、aその型の配列に割り当て、aその型の変数にから要素を割り当てることができます。つまり、配列配列の値は正しいタイプです。

これは、Javaチュートリアルで説明されているように、クラスリテラルをランタイムタイプトークンとして使用することで機能します。クラスリテラルは、コンパイラによってのインスタンスとして扱われますjava.lang.Class。使用するには、クラス名の後にを付け.classます。したがって、クラスを表すオブジェクトString.classとして機能しClassますString。これは、インターフェイス、列挙型、任意の次元の配列(例String[].class)、プリミティブ(例int.class)、およびキーワードvoid(例)でも機能しますvoid.class

Classそれ自体は総称です(として宣言されClass<T>TClassオブジェクトが表す型を表します)。つまり、の型はString.classですClass<String>

したがって、のコンストラクターを呼び出すときは常にGenSetGenSetインスタンスの宣言された型の配列を表す最初の引数にクラスリテラルを渡します(たとえばString[].classGenSet<String>)。プリミティブは型変数に使用できないため、プリミティブの配列を取得できないことに注意してください。

コンストラクター内でメソッドを呼び出すと、cast渡されたObject引数Classが、メソッドが呼び出されたオブジェクトによって表されるクラスにキャストされます。静的メソッドnewInstanceを呼び出すと、最初の引数として渡されたオブジェクトによって表される型の配列と、2番目の引数として渡されたオブジェクトによって指定された長さの配列java.lang.reflect.Arrayとして返されます。メソッドを呼び出す返しで表される配列のコンポーネントタイプを表すオブジェクトのオブジェクトは、例えば、(どのメソッドが呼び出されたため、場合にObjectClassintgetComponentTypeClassClassString.classString[].classnullClassオブジェクトが配列を表していません)。

最後の文は完全に正確ではありません。呼び出すと、クラスを表すオブジェクトString[].class.getComponentType()が返されますが、その型はではなくです。そのため、次のようなことはできません。ClassStringClass<?>Class<String>

String foo = String[].class.getComponentType().cast("bar"); // won't compile

オブジェクトClassを返すすべてのメソッドについても同様Classです。

この回答に対する Joachim Sauerのコメントについて(私は自分でコメントするのに十分な評判がありません)、キャストを使用する例T[]では、コンパイラーが型の安全性を保証できないため、警告が表示されます。


Ingoのコメントに関する編集:

public static <T> T[] newArray(Class<T[]> type, int size) {
   return type.cast(Array.newInstance(type.getComponentType(), size));
}

5
これは役に立たない、それは新しい文字列[...]を書くための複雑な方法にすぎません。しかし、本当に必要なのはpublic static <T> T [] newArray(int size){...}のようなものであり、これは単にjava noirには存在せず、リフレクションでシミュレートできます-その理由は、インスタンス化されたジェネリック型は実行時に使用できません。
Ingo

4
@Ingo何のことをいっているのですか。私のコードは、任意のタイプの配列を作成するために使用できます。
gdejohn

3
@Charlatan:もちろんですが、新しい[]もできます。問題は、誰がいつそのタイプを知っているかです。したがって、ジェネリック型しか持っていない場合はできません。
Ingo

2
それは間違いない。で、あなたはジェネリック型X用の実行時にクラスのオブジェクトを取得しないポイント
インゴ・

2
ほとんど。これはnew []で実現できる以上のものであることを認めます。実際には、ほとんどの場合これで十分です。ただし、たとえば、メソッドE [] toArray()を持ち、実際に真のE []配列を返す、Eでパラメーター化されたコンテナークラスを作成することはまだ不可能です。コードは、コレクションに少なくとも1つのEオブジェクトがある場合にのみ適用できます。したがって、一般的な解決策は不可能です。
Ingo

42

これはタイプセーフな唯一の答えです

E[] a;

a = newArray(size);

@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
    return Arrays.copyOf(array, length);
}

私はそれを調べなければなりませんでしたが、はい、2番目の「長さ」引数Arrays#copyOf()は、最初の引数として指定された配列の長さとは無関係です。すなわち、それはへの呼び出しのコストを支払うんが、巧妙だMath#min()System#arrayCopy()厳密にこの仕事を成し遂げるために必要などちらも、。docs.oracle.com/javase/7/docs/api/java/util/...
SEH

8
E型変数の場合、これは機能しません。varargs は、が型変数であるE場合の消去の配列を作成し、Eとそれほど変わりません(E[])new Object[n]http://ideone.com/T8xF91を参照してください。それは決して、他の回答よりも多くのタイプの安全。
Radiodef、2015

1
@Radiodef-ソリューションはコンパイル時にタイプセーフであることが証明されています。消去は言語仕様の一部ではないことに注意してください。仕様は慎重に記述されているので、将来は完全に具体化することができます。他のソリューションとは異なり、このソリューションは実行時にも完全に機能します。
ZhongYu

@Radiodef-一般的な配列の作成を禁止することは良い考えかどうかは議論の余地があります。それにもかかわらず、言語はバックドアを残します-varargには一般的な配列の作成が必要です。言語が許可したかのように良いnew E[]です。あなたの例で示した問題は、一般的な消去の問題であり、この質問とこの回答に固有のものではありません。
ZhongYu

2
@Radiodef-いくつかの違いがあります。このソリューションの正確性はコンパイラーによってチェックされます。強制キャストの人間の推論には依存しません。この特定の問題では、違いは重要ではありません。少し空想を好む人もいます、それだけです。OPの言い回しに誤解されている場合は、コメントと私の説明で明確になります。
ZhongYu

33

より多くの次元に拡張するには、に[]次元パラメータを追加するだけですnewInstance()Tisは型パラメータ、clsis Class<T>d1through d5は整数):

T[] array = (T[])Array.newInstance(cls, d1);
T[][] array = (T[][])Array.newInstance(cls, d1, d2);
T[][][] array = (T[][][])Array.newInstance(cls, d1, d2, d3);
T[][][][] array = (T[][][][])Array.newInstance(cls, d1, d2, d3, d4);
T[][][][][] array = (T[][][][][])Array.newInstance(cls, d1, d2, d3, d4, d5);

詳細Array.newInstance()については、を参照してください。


4
+1多次元配列の作成について、この投稿の誤解としてクローズされる質問がありましたが、具体的に対処された回答はありませんでした。
Paul Bellora 2013

1
@JordanC多分; 精神的にはstackoverflow.com/a/5671304/616460と同じですが、明日をどう扱うかを考えます。眠いです。
Jason C

14

Java 8では、ラムダまたはメソッド参照を使用して、一種の汎用配列を作成できます。これはリフレクティブアプローチ(を渡すClass)に似ていますが、ここではリフレクションを使用していません。

@FunctionalInterface
interface ArraySupplier<E> {
    E[] get(int length);
}

class GenericSet<E> {
    private final ArraySupplier<E> supplier;
    private E[] array;

    GenericSet(ArraySupplier<E> supplier) {
        this.supplier = supplier;
        this.array    = supplier.get(10);
    }

    public static void main(String[] args) {
        GenericSet<String> ofString =
            new GenericSet<>(String[]::new);
        GenericSet<Double> ofDouble =
            new GenericSet<>(Double[]::new);
    }
}

たとえば、これはによって使用され<A> A[] Stream.toArray(IntFunction<A[]>)ます。

これは、 Java 8より前の匿名クラスを使用して行うことできますが、より面倒です。


ArraySupplierこのような特別なインターフェースは実際には必要ありません。コンストラクターを宣言GenSet(Supplier<E[]> supplier) { ...して、同じ行で呼び出すことができます。
Lii 2015

4
@Lii私の例と同じになるとIntFunction<E[]>、それはになりますが、そうです。
Radiodef、2015

11

これは、Effective Java、2nd Editionの第5章(Generics)の項目25 ...で説明されています。配列を優先する

コードは機能しますが、チェックされていない警告が生成されます(次の注釈で抑制できます:

@SuppressWarnings({"unchecked"})

ただし、配列の代わりにリストを使用する方が良いでしょう。

このバグ/機能に関する興味深い議論がOpenJDKプロジェクトサイトにあります。


8

Class引数をコンストラクタに渡す必要はありません。これを試して。

public class GenSet<T> {
    private final T[] array;
    @SuppressWarnings("unchecked")
    public GenSet(int capacity, T... dummy) {
        if (dummy.length > 0)
            throw new IllegalArgumentException(
              "Do not provide values for dummy argument.");
        Class<?> c = dummy.getClass().getComponentType();
        array = (T[])Array.newInstance(c, capacity);
    }
    @Override
    public String toString() {
        return "GenSet of " + array.getClass().getComponentType().getName()
            + "[" + array.length + "]";
    }
}

そして

GenSet<Integer> intSet = new GenSet<>(3);
System.out.println(intSet);
System.out.println(new GenSet<String>(2));

結果:

GenSet of java.lang.Integer[3]
GenSet of java.lang.String[2]

7

Javaジェネリックは、コンパイル時に型をチェックし、適切なキャストを挿入することで機能しますが、コンパイル済みファイルの型を消去します。これにより、ジェネリックを理解しないコード(意図的な設計上の決定でした)でジェネリックライブラリを使用できるようになりますが、通常、実行時に型が何であるかを確認できません。

パブリックStack(Class<T> clazz,int capacity)コンストラクタは、クラスの情報が意味し、実行時にクラスのオブジェクトを渡す必要がありますされ、それを必要とするコードの実行時に利用できます。そしてそのClass<T>形式は、コンパイラが、渡したClassオブジェクトがT型のClassオブジェクトであることを確認することを意味します。Tのサブクラスではなく、Tのスーパークラスではなく、正確にTです。

これは、コンストラクターで適切な型の配列オブジェクトを作成できることを意味します。つまり、コレクションに格納するオブジェクトの型は、コレクションに追加された時点で型がチェックされます。


6

こんにちはスレッドは死んでいますが、これに注意を向けたいと思います:

Genericsは、コンパイル時の型チェックに使用されます。

  • したがって、目的は、入ってくるものが必要なものであることを確認することです。
  • あなたが返すものは、消費者が必要とするものです。
  • これをチェックして:

ここに画像の説明を入力してください

ジェネリッククラスを作成するときに、型キャストの警告について心配する必要はありません。使用時の心配です。


6

このソリューションはどうですか?

@SafeVarargs
public static <T> T[] toGenericArray(T ... elems) {
    return elems;
}

それは機能し、単純すぎて本当ではないように見えます。欠点はありますか?


3
きちんと動作しますが、「手動」で呼び出す場合にのみ機能します。つまり、要素を個別に渡します。の新しいインスタンスを作成できない場合T[]、プログラムでT[] elems関数を作成して関数に渡すことはできません。そしてできれば、関数は必要ありません。
orlade

5

このコードも見てください:

public static <T> T[] toArray(final List<T> obj) {
    if (obj == null || obj.isEmpty()) {
        return null;
    }
    final T t = obj.get(0);
    final T[] res = (T[]) Array.newInstance(t.getClass(), obj.size());
    for (int i = 0; i < obj.size(); i++) {
        res[i] = obj.get(i);
    }
    return res;
}

任意の種類のオブジェクトのリストを同じタイプの配列に変換します。


はい、あなたはnullを返します、それは期待された空の配列ではありません。それはあなたができる最高ですが、理想的ではありません。
Kevin Cox

がスローListするなど、オブジェクトに複数のタイプのオブジェクトが含まれている場合も、これが失敗することがあります。toArray(Arrays.asList("abc", new Object()))ArrayStoreException
Radiodef 2015

私はこれを取り除いたバージョンを使用しました。私が最初に使用できたのはうまくいきましたが、確かに私はもっと複雑な解決策のいくつかを試していませんでした。各インデックスに同じ値が必要だったので、forループやその他を回避するために使用しましたArrays.fill(res, obj);
bbarker

5

私はすばやく簡単に機能する方法を見つけました。これはJava JDK 8でのみ使用していることに注意してください。以前のバージョンで動作するかどうかはわかりません。

特定の型パラメーターのジェネリック配列をインスタンス化することはできませんが、作成済みの配列をジェネリッククラスコンストラクターに渡すことができます。

class GenArray <T> {
    private T theArray[]; // reference array

    // ...

    GenArray(T[] arr) {
        theArray = arr;
    }

    // Do whatever with the array...
}

これで、メインで次のように配列を作成できます。

class GenArrayDemo {
    public static void main(String[] args) {
        int size = 10; // array size
        // Here we can instantiate the array of the type we want, say Character (no primitive types allowed in generics)
        Character[] ar = new Character[size];

        GenArray<Character> = new Character<>(ar); // create the generic Array

        // ...

    }
}

配列の柔軟性を高めるには、リンクリストを使用できます。ArrayListおよびJava.util.ArrayListクラスにあるその他のメソッド。


4

この例では、Javaリフレクションを使用して配列を作成しています。タイプセーフではないため、これを行うことは一般に推奨されません。代わりに、内部リストを使用して、配列をまったく回避する必要があります。


13
2番目の例(Array.newInstance()を使用)、実際にはタイプセーフです。これが可能なのは、Classオブジェクトの型Tが配列のTと一致する必要があるためです。基本的には、Javaランタイムがジェネリックについて破棄する情報を提供することを強制します。
ヨアヒムザウアー


3

このコードスニペットを作成して、簡単な自動テストユーティリティに渡されるクラスを反射的にインスタンス化しました。

Object attributeValue = null;
try {
    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }
    else if(!clazz.isInterface()){
        attributeValue = BeanUtils.instantiateClass(clazz);
    }
} catch (Exception e) {
    logger.debug("Cannot instanciate \"{}\"", new Object[]{clazz});
}

このセグメントに注意してください:

    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }

アレイは、ここで開始するArray.newInstance(アレイのクラス、アレイのサイズ)。クラスは、プリミティブ(int.class)とオブジェクト(Integer.class)の両方にすることができます。

BeanUtilsはSpringの一部です。


3

実際にこれを行うには、次の例のようにオブジェクトの配列を作成し、それを目的の型にキャストするのがより簡単です。

T[] array = (T[])new Object[SIZE];

ここで、SIZE定数であり、Tタイプ識別子であります


1

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

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

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

ここで、Itemは、メンバーを含む私が定義したクラスです。

private K value;

このようにして、K型の配列(項目に値しかない場合)またはクラスItemで定義したい任意のジェネリック型を取得します。


1

あなたが投稿した例で何が起こっているのかという質問に誰も答えていません。

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

他の人が言ったように、ジェネリックはコンパイル中に「消去」されます。したがって、実行時にジェネリックのインスタンスは、そのコンポーネントタイプが何であるかを知りません。この理由は歴史的なものであり、Sunは既存のインターフェース(ソースとバイナリの両方)を壊すことなくジェネリックを追加したいと考えていました。

一方、配列、実行時にそのコンポーネントのタイプを知っています。

この例では、コンストラクター(型を知っている)を呼び出すコードに、必要な型をクラスに通知するパラメーターを渡すことで、問題を回避しています。

したがって、アプリケーションは次のようなクラスを構築します

Stack<foo> = new Stack<foo>(foo.class,50)

コンストラクターはコンポーネントのタイプを(実行時に)認識し、その情報を使用してリフレクションAPIを通じて配列を構築できます。

Array.newInstance(clazz, capacity);

最後に、型キャストがあります。コンパイラが返す配列Array#newInstance()が正しい型であることを知っているからです(わかっていても)。

このスタイルは少し見苦しいですが、何らかの理由で配列を作成したり、コンポーネントタイプのインスタンスを作成したりするなど、実行時にコンポーネントタイプを知る必要があるジェネリックタイプを作成する場合、最も悪い解決策になることがあります。


1

私はこの問題の一種の回避策を見つけました。

以下の行は一般的な配列作成エラーをスローします

List<Person>[] personLists=new ArrayList<Person>()[10];

ただしList<Person>、別のクラスにカプセル化しても機能します。

import java.util.ArrayList;
import java.util.List;


public class PersonList {

    List<Person> people;

    public PersonList()
    {
        people=new ArrayList<Person>();
    }
}

getterを使用して、PersonListクラスの人々を公開できます。以下の行は、List<Person>すべての要素にがある配列を提供します。つまり、の配列ですList<Person>

PersonList[] personLists=new PersonList[10];

私が取り組んでいるいくつかのコードでこのようなものが必要でした、そしてこれはそれを機能させるために私がやったことです。これまでのところ問題はありません。


0

Object配列を作成して、どこでもEにキャストできます。うん、それはそれを行うにはあまりきれいな方法ではありませんが、それは少なくともうまくいくはずです。


「私たちは、いくつかの説明とコンテキストを提供する長い回答を探しています。1行の回答を与えるだけでなく、あなたの回答が正しい理由を説明してください。
gparyani 2014

ただし、ジェネリッククラスがComparableインターフェースを実装する場合など、場合によっては機能しません。
RamPrasadBismil

7年前へようこそ。
Esko

1
ジェネリックコードからジェネリック以外の呼び出し元に配列を返そうとすると、これは機能しません。ヘッドスクレイティングクラスキャスト例外があります。
プラグウォッシュ2017

0

これを試して。

private int m = 0;
private int n = 0;
private Element<T>[][] elements = null;

public MatrixData(int m, int n)
{
    this.m = m;
    this.n = n;

    this.elements = new Element[m][n];
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
        {
            this.elements[i][j] = new Element<T>();
        }
    }
}

コードを実行できません。Elementクラスはどこから来たのですか?

0

これに対する簡単で厄介な回避策は、メインクラス内に2つ目の「ホルダー」クラスをネストし、それを使用してデータを保持することです。

public class Whatever<Thing>{
    private class Holder<OtherThing>{
        OtherThing thing;
    }
    public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10]
}

3
これは実際には機能しません。new Holder<Thing>[10]ジェネリック配列の作成です。
Radiodef 2014年

0

この質問とは関係ないかもしれませんが、使用中に「generic array creation」エラーが発生しました

Tuple<Long,String>[] tupleArray = new Tuple<Long,String>[10];

私は以下の作品を見つけました(そして私のために働いた) @SuppressWarnings({"unchecked"})

 Tuple<Long, String>[] tupleArray = new Tuple[10];

ええ、これは完全に関連しているわけではありませんが、同じ問題(消去、配列の共分散)に根差しています。ここではパラメータ化された型の配列を作成する方法についてポストの例です:stackoverflow.com/questions/9542076/...
ポールBellora

0

このコードが効果的なジェネリック配列を作成するかどうか疑問に思っていますか?

public T [] createArray(int desiredSize){
    ArrayList<T> builder = new ArrayList<T>();
    for(int x=0;x<desiredSize;x++){
        builder.add(null);
    }
    return builder.toArray(zeroArray());
}

//zeroArray should, in theory, create a zero-sized array of T
//when it is not given any parameters.

private T [] zeroArray(T... i){
    return i;
}

編集:おそらく、そのような配列を作成する別の方法は、必要なサイズがわかっていて小さい場合、必要な数の「null」をzeroArrayコマンドに単純にフィードすることでしょうか?

もちろん、これはcreateArrayコードを使用する場合ほど用途が広いわけではありません。


いいえ、これは機能しません。varargs は、型変数であるTときの消去を作成します。Tつまり、をzeroArray返しますObject[]http://ideone.com/T8xF91を参照してください。
Radiodef、2015

0

あなたはキャストを使うことができます:

public class GenSet<Item> {
    private Item[] a;

    public GenSet(int s) {
        a = (Item[]) new Object[s];
    }
}

これを提案する場合は、その制限について本当に説明する必要があります。aクラスの外に公開しないでください!
Radiodef、2015

0

私は実際に、ジェネリックアレイを開始できないことを回避するかなりユニークなソリューションを見つけました。あなたがしなければならないことは、次のようにジェネリック変数Tを受け取るクラスを作成することです:

class GenericInvoker <T> {
    T variable;
    public GenericInvoker(T variable){
        this.variable = variable;
    }
}

そして、あなたの配列クラスでそれを次のように始めてください:

GenericInvoker<T>[] array;
public MyArray(){
    array = new GenericInvoker[];
}

を開始するnew Generic Invoker[]と、チェックされていない状態で問題が発生しますが、実際には問題はありません。

配列から取得するには、次のようにarray [i] .variableを呼び出す必要があります。

public T get(int index){
    return array[index].variable;
}

配列のサイズ変更などの残りは、次のようにArrays.copyOf()で実行できます。

public void resize(int newSize){
    array = Arrays.copyOf(array, newSize);
}

そしてadd関数は次のように追加できます:

public boolean add(T element){
    // the variable size below is equal to how many times the add function has been called 
    // and is used to keep track of where to put the next variable in the array
    arrays[size] = new GenericInvoker(element);
    size++;
}

1
問題はT、パラメーター化された型の配列ではなく、ジェネリック型パラメーターの型の配列を作成することでした。
Sotirios Delimanolis 2017年

ただし、同じタスクを完了し、クラスをプッシュインする必要がないため、カスタムコレクションが使いやすくなります。
カニ星雲

何の仕事?文字通り異なるタスクです。パラメータ化された型の配列とジェネリック型パラメータの配列です。
Sotirios Delimanolis 2017年

ジェネリック型から配列を作成できますか?元の問題は、ジェネリック型を使用して配列を初期化することでした。このメソッドを使用すると、ユーザーがクラスをプッシュしたり、オブジェクトを文字列にキャストしようとするなどの未チェックのエラーを発生させたりせずに実行できます。チルのように、私は自分の仕事が得意ではありません。プログラミングの学校に行ったことはありませんが、インターネットで他の子供に知らされるのではなく、少しの意見に値すると思います。
カニ星雲2017年

ソチロスに同意します。答えを考えるには2つの方法があります。別の質問への回答であるか、質問を一般化する試みです。どちらも間違っている/役に立たない。「ジェネリック配列」クラスを実装する方法についてのガイダンスを探している人は、質問のタイトルを読んだときに読み上げを停止するでしょう。そして、彼らが30の回答を持つQを見つけたとき、彼らは最後までスクロールして、SOの初心者からゼロ投票の回答を読むことはほとんどありません。
スティーブンC

0

vnportnoyによると、構文

GenSet<Integer> intSet[] = new GenSet[3];

次のように入力されるnull参照の配列を作成します

for (int i = 0; i < 3; i++)
{
   intSet[i] = new GenSet<Integer>();
}

これはタイプセーフです。


-1
private E a[];
private int size;

public GenSet(int elem)
{
    size = elem;
    a = (E[]) new E[size];
}

常にコードに説明を追加し、投稿された元の質問を解決する理由を説明する必要があります。
mjuarez

-1

ジェネリック配列の作成はJavaでは許可されていませんが、次のようにできます

class Stack<T> {
private final T[] array;
public Stack(int capacity) {
    array = (T[]) new Object[capacity];
 }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.