Voici comment utiliser les génériques pour obtenir un tableau du type précis que vous recherchez tout en préservant la sécurité du type (contrairement aux autres réponses, qui vous redonneront un Objecttableau ou entraîneront des avertissements au moment de la compilation):
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];
}
}
Cela se compile sans avertissements, et comme vous pouvez le voir main, pour n'importe quel type dont vous déclarez une instance GenSetas, vous pouvez l'affecter aà un tableau de ce type, et vous pouvez affecter un élément de aà une variable de ce type, ce qui signifie que le tableau et les valeurs du tableau sont du type correct.
Il fonctionne en utilisant des littéraux de classe comme jetons de type runtime, comme indiqué dans les didacticiels Java . Les littéraux de classe sont traités par le compilateur comme des instances de java.lang.Class. Pour en utiliser un, suivez simplement le nom d'une classe avec .class. Agit donc String.classcomme un Classobjet représentant la classe String. Cela fonctionne également pour les interfaces, les énumérations, les tableaux de n'importe quelle dimension (par exemple String[].class), les primitives (par exemple int.class) et le mot clé void(par exemple void.class).
Classlui-même est générique (déclaré comme Class<T>, où Treprésente le type que l' Classobjet représente), ce qui signifie que le type de String.classest Class<String>.
Ainsi, chaque fois que vous appelez le constructeur pour GenSet, vous passez un littéral de classe pour le premier argument représentant un tableau du GenSettype déclaré de l' instance (par exemple String[].classpour GenSet<String>). Notez que vous ne pourrez pas obtenir un tableau de primitives, car les primitives ne peuvent pas être utilisées pour les variables de type.
A l'intérieur du constructeur, l'appel de la méthode castrenvoie l' Objectargument passé cast à la classe représentée par l' Classobjet sur lequel la méthode a été appelée. L'appel de la méthode statique newInstancedans java.lang.reflect.Arrayrenvoie comme un Objecttableau du type représenté par l' Classobjet passé comme premier argument et de la longueur spécifiée par intpassé comme deuxième argument. Appel de la méthodegetComponentType renvoie un Classobjet représentant le type de composant de la matrice représentée par l' Classobjet sur lequel la méthode a été appelée (par exemple , String.classpour String[].class, nullsi l' Classobjet ne représente pas un tableau).
Cette dernière phrase n'est pas tout à fait exacte. L'appel String[].class.getComponentType()renvoie unClass objet représentant la classe String, mais son type ne l'est Class<?>pas Class<String>, c'est pourquoi vous ne pouvez pas faire quelque chose comme ce qui suit.
String foo = String[].class.getComponentType().cast("bar"); // won't compile
Il en va de même pour chaque méthode Classqui renvoie un Classobjet.
Concernant le commentaire de Joachim Sauer sur cette réponse (je n'ai pas assez de réputation pour le commenter moi-même), l'exemple utilisant le cast pour T[]entraînera un avertissement car le compilateur ne peut pas garantir la sécurité du type dans ce cas.
Modifier concernant les commentaires d'Ingo:
public static <T> T[] newArray(Class<T[]> type, int size) {
return type.cast(Array.newInstance(type.getComponentType(), size));
}