Je comprends les génériques en matière de collections. Mais qu'est-ce que cela signifie dans le cas de la classe Class<T>
? Lorsque vous instanciez un objet Class
, il n'y a qu'un seul objet. Alors pourquoi le paramètre T
? Que spécifie-t-il? Et pourquoi est-ce nécessaire (si c'est le cas)?
Le paramètre de type <T>
a été ajouté à Java.lang.Class
pour activer un idiome spécifique1 - utilisation d'objets Class
en tant que fabriques d'objets de type sécurisé. L'ajout de <T>
permet essentiellement d'instancier les classes de manière sécurisée, comme ceci:
T instance = myClass.newInstance();
Le paramètre de type <T>
représente la classe elle-même, ce qui vous permet d'éviter les effets désagréables de l'effacement du type en stockant Class<T>
dans une classe générique ou en le transmettant en tant que paramètre à une méthode générique. Notez que T
ne serait pas suffisant en soi pour effectuer cette tâche.2: le type de T
est effacé, il devient donc Java.lang.Object
sous le capot.
Voici un exemple classique où le paramètre <T>
de la classe devient important. Dans l'exemple ci-dessous, le compilateur Java est capable de garantir la sécurité des types en vous permettant de générer une collection typée à partir d'une chaîne SQL et d'une instance de Class<T>
. Notez que la classe est utilisée en tant que factory et que son type de sécurité peut être vérifié lors de la compilation:
public static <T> Collection<T> select(Class<T> c, String sqlStatement) {
Collection<T> result = new ArrayList<T>();
/* run sql query using jdbc */
for ( /* iterate over jdbc results */ ) {
T item = c.newInstance();
/* use reflection and set all of item’s fields from sql results */
result.add(item);
}
return result;
}
Etant donné que Java efface le paramètre type, ce qui en fait un Java.lang.Object
ou une classe spécifiée en tant que borne supérieure du générique, il est important d'avoir accès à l'objet Class<T>
dans la méthode select
. Étant donné que newInstance
renvoie un objet de type <T>
, le compilateur peut effectuer une vérification de type en éliminant une conversion.
Le answer de dasblinkenlight a déjà démontré l’une des utilisations principales de ce paramètre. Il y a un autre aspect que je considère pertinent: en utilisant ce paramètre, vous pouvez restreindre le type de classe que vous souhaitez transmettre à un emplacement donné. Donc, par exemple.
Class<? extends Number> cls
cela signifie que cls
peut être toute classe implémentant l'interface Number
. Cela peut aider à détecter certaines erreurs lors de la compilation et à rendre plus explicites les exigences des arguments de classe.
Peut-être une comparaison avec le cas sans génériques est en ordre
// Java ≥5 with generics // Java <5 style without generics
Class<? extends Foo> c; Class c;
Foo t1 = c.newInstance(); Foo t1 = (Foo)c.newInstance();
Object obj; Object obj;
Foo t2 = c.cast(obj); Foo t2 = (Foo)c.cast(obj);
Comme vous pouvez le constater, ne pas avoir comme argument T
nécessiterait un certain nombre d'incantations explicites, car les méthodes correspondantes devraient renvoyer Object
au lieu de T
. Si Foo
est lui-même un argument de type générique, alors tous ces lancements seront décochés, ce qui entraînera une séquence d'avertissements du compilateur. Vous pouvez les supprimer, mais le problème principal demeure: le compilateur ne peut pas vérifier la validité de ces conversions à moins que vous n'utilisiez correctement l'argument de type.
En Java, il existe une seule métaclasse: Class
. Ses instances (une seule par type existe) sont utilisées pour représenter les classes et les interfaces. Par conséquent, T
dans Class<T>
fait référence au type de la classe ou de l'interface que représente l'instance actuelle de Class
.
L'utilisation de génériques dans le type de classe est top pour définir le type de classe. Si j'ai 'Class obj', mon objet obj ne peut contenir que des enfants de Charsequence ..__ Ceci est un argument optionnel Je mets souvent un '?' pour éviter les avertissements d'Eclipse IDE si je n'ai pas besoin d'un type spécifique de classe.