Je comprends les génériques quand il s'agit de collections. Mais qu'est-ce que cela signifie dans le cas des Class<T>
classe ? Lorsque vous instanciez une Class
objet, il n'y a qu'un seul objet. Alors pourquoi le T
paramètre ? Que spécifie-t-il ? Et pourquoi est-il nécessaire (s'il l'est) ?
Réponses
Trop de publicités?Paramètre de type <T>
a été ajouté à java.lang.Class
pour permettre un idiome spécifique 1 - l'utilisation de Class
comme des fabriques d'objets à sécurité intrinsèque. Essentiellement, l'ajout de <T>
vous permet d'instancier des classes d'une manière sûre, comme ceci :
T instance = myClass.newInstance();
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 de type en stockant Class<T>
dans une classe générique ou en le passant comme paramètre à une méthode générique. Notez que T
ne suffirait pas à lui seul à accomplir cette tâche. 2 : le type de T
est effacé, il devient donc java.lang.Object
sous le capot.
Voici un exemple classique où <T>
Le paramètre de la classe devient important. Dans l'exemple ci-dessous, le compilateur Java est capable d'assurer la sécurité de type, vous permettant de produire une collection typée à partir d'une chaîne SQL et d'une instance de Class<T>
. Notez que la classe est utilisée comme un usine et que sa sécurité de type peut être vérifiée au moment 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;
}
Étant donné que Java efface le paramètre de type, ce qui en fait une java.lang.Object
ou une classe spécifiée en tant que limite supérieure il est important d'avoir accès à la Class<T>
à l'intérieur de l'objet select
méthode. Puisque newInstance
renvoie un objet de type <T>
le compilateur peut effectuer une vérification de type, éliminant ainsi un cast.
1 SUN Oracle a publié un bon article expliquant tout cela .
2 Ceci est différent des implémentations de génériques sans effacement de type, comme celle de .NET.
3 Tutoriel Java Generics par Oracle.
Le site réponse de dasblinkenlight a déjà démontré l'une des principales utilisations 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 voulez passer à un endroit donné. Ainsi, par exemple
Class<? extends Number> cls
signifie que cls
peut être n'importe quelle classe implémentant le Number
interface. Cela peut aider à détecter certaines erreurs au moment de la compilation et rend les exigences relatives aux arguments de classe plus explicites.
Peut-être qu'une comparaison avec le cas sans génériques s'impose.
// 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 voir, ne pas avoir T
comme argument nécessiterait un certain nombre de castings explicites, car les méthodes correspondantes devraient retourner Object
au lieu de T
. Si Foo
est lui-même un argument de type générique, alors tous ces casts ne seraient pas vérifiés, ce qui entraînerait une série 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 casts si vous n'utilisez pas correctement l'argument de type.
L'utilisation de génériques dans le type de classe permet de définir le type de la classe. Si j'ai ' Class obj' mon objet obj ne peut contenir que des enfants de Charsequence. C'est un argument optionnel. Je mets souvent un '?' pour éviter les avertissements de l'IDE Eclipse si je n'ai pas besoin d'un type de classe spécifique.