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 Object
tableau 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 GenSet
as, 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.class
comme un Class
objet 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
).
Class
lui-même est générique (déclaré comme Class<T>
, où T
représente le type que l' Class
objet représente), ce qui signifie que le type de String.class
est 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 GenSet
type déclaré de l' instance (par exemple String[].class
pour 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 cast
renvoie l' Object
argument passé cast à la classe représentée par l' Class
objet sur lequel la méthode a été appelée. L'appel de la méthode statique newInstance
dans java.lang.reflect.Array
renvoie comme un Object
tableau du type représenté par l' Class
objet passé comme premier argument et de la longueur spécifiée par int
passé comme deuxième argument. Appel de la méthodegetComponentType
renvoie un Class
objet représentant le type de composant de la matrice représentée par l' Class
objet sur lequel la méthode a été appelée (par exemple , String.class
pour String[].class
, null
si l' Class
objet 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 Class
qui renvoie un Class
objet.
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));
}