L'objectif principal est de restreindre une collection ou une structure à un type précis, offrant ainsi une vérification de type dès la compilation plutôt qu'à l'exécution. À noter que les méthodes statiques d'une classe générique ne peuvent exploiter les paramètres de type déclarés au niveau de la classe elle-même.
Héritage et classes génériques
Lorsqu'une sous-classe hérite d'une classe parent générique, plusieurs scénarios sont possibles :
Spécification explicite du type : la sous-classe n'est plus générique.
public class CommandeSpeciale extends Commande<Integer> {
// CommandeSpeciale n'est pas une classe générique
}
Conservation du paramètre de type : la sous-classe demeure générique.
public class CommandeGenerique<T> extends Commande<T> {
// CommandeGenerique reste paramétrable
}
Illustration complète avec un parent à deux paramètres :
class Parent<K, V> { }
// Effacement total des types
class FilsA extends Parent {
// équivaut à Parent<Object, Object>
}
// Fixation des deux types
class FilsB extends Parent<Integer, String> { }
// Conservation intégrale
class FilsC<K, V> extends Parent<K, V> { }
// Conservation partielle
class FilsD<V> extends Parent<Integer, V> { }
Méthodes génériques
Une méthode générique déclare son propre paramètre de type, indépendant de ceux de sa classe conteneur. Sa déclaration se place avant le type de retour. Contrairement aux variables d'instance génériques, les méthodes génériques peuvent être statiques car leur type est résolu lors de l'appel.
public static <T> List<T> transformerTableauEnListe(T[] tableau) {
List<T> resultat = new ArrayList<>();
for (T element : tableau) {
resultat.add(element);
}
return resultat;
}
Le wildcard (joker) ?
Le symbole ? représente un type inconnu. Il sert de dénominateur commun entre différentes instanciations d'un même type générique. Par exemple, bien que List<Object> et List<String> soient sans relation de parenté directe, List<?> fait office de supertype commun. Une collection ainsi typée accepte la lecture (type de retour Object) mais refuse toute insertion, à l'exception de null.
@Test
public void demonstrationWildcard() {
List<Object> objets = null;
List<String> chaines = null;
List<?> listeGenerique = null;
listeGenerique = objets; // valide
listeGenerique = chaines; // valide
List<String> noms = new ArrayList<>();
noms.add("Alice");
noms.add("Bob");
listeGenerique = noms;
// Insertion interdite sauf null
listeGenerique.add(null);
// listeGenerique.add("Test"); // erreur de compilation
// Lecture autorisée, type de retour : Object
Object premier = listeGenerique.get(0);
System.out.println("Taille : " + listeGenerique.size());
System.out.println(premier);
}
Wildcards bornés
Borne supérieure (? extends T) : accepte T ou ses sous-types. La lecture renvoie un élément de type T, mais toute insertion est interdite car le type exact ne peut être garanti.
Borne inférieure (? super T) : accepte T ou ses super-types. L'insertion d'éléments de type T ou de ses sous-types est autorisée, mais la lecture renvoie Object.
@Test
public void demonstrationBornes() {
List<? extends Personne> limiteHaute = null;
List<? super Personne> limiteBasse = null;
List<Etudiant> etudiants = new ArrayList<>();
List<Personne> personnes = new ArrayList<>();
List<Object> objets = new ArrayList<>();
// ? extends Personne : accepte Etudiant et Personne
limiteHaute = etudiants;
limiteHaute = personnes;
// limiteHaute = objets; // refusé
// ? super Personne : accepte Personne et Object
// limiteBasse = etudiants; // refusé
limiteBasse = personnes;
limiteBasse = objets;
// Lecture avec borne supérieure
limiteHaute = etudiants;
Personne p = limiteHaute.get(0);
// Etudiant e = limiteHaute.get(0); // échec à la compilation
// Lecture avec borne inférieure
limiteBasse = personnes;
Object o = limiteBasse.get(0);
// Personne p2 = limiteBasse.get(0); // échec à la compilation
// Écriture avec borne supérieure : interdite
// limiteHaute.add(new Etudiant()); // erreur
// Écriture avec borne inférieure : autorisée pour les sous-types
limiteBasse.add(new Personne());
limiteBasse.add(new Etudiant());
}