Découverte de la Généricité par l’exemple
Introduction
Vous avez découvert que l’on pouvait avoir un ?
, un T
et aussi un E
dans le code Java pour exprimer la généricité, mais pour tout vous dire, ce ne sont que des conventions de
nommage qui cependant sont bien pratiques, voici résumé, leurs spécificités et le sens à leur donner :
- Le point d’interrogation
?
est utilisé pour spécifier un type variable qui peut prendre n’importe quel type. On peut écrireArrayList<?>
pour indiquer que la liste peut être de n’importe quel type. - La lettre
E
majuscule est utilisée pour définir un type générique pour les collections. On voit souventList<E>
pour exprimer qu’il s’agit d’une liste d’Eléments. - La lettre
T
majuscule est souvent utilisé pour indiquer un type générique pour les classes et les interfaces génériques. par exempleBoite<T>
.
Vous n’avez que l’embarras du choix ;)
Quoi faire alors ? Je ne sais pas quoi choisir
Finalement, vous pouvez mettre ce que vous voulez. Les lettres E
et T
sont souvent utilisées comme convention mais vous l’avez compris, il n’y a pas de règles strictes à suivre !
On peut utiliser ce que l’on veut, une autre lettre ou même un nom de variable.
D’ailleurs, lorsque l’on utilise une Collection de type clef et valeur, on trouve souvent ceci : Les lettres K
pour Key et V
pour value, juste une histoire de convention et de faciliter de lecture !
Quelles sont les limites, que peut-on ne pas faire ?
- Impossible de créer des instances de types génériques. On ne peut pas écrire new T() ni même new T[] car le compilateur ne sait pas quel type de données il doit créer !
- Opérations mathématiques non autorisées. On ne peut pas faire x + y si x et y sont des types génériques.
- On ne peut pas créer des tableaux de types génériques. Ecrire T[] tab = new T[3000]; est impossible car le compilateur ne sait pas quel type de données utiliser pour la création du tableau.
- Impossible d’utiliser les opérateurs de comparaison comme == et !=. Il faut utiliser les méthodes equals() ou compareTo().
- On ne peut pas non plus créer des exceptions génériques. Les exceptions sont toujours associées à des types spécifiques.
- On ne peut pas utiliser des types génériques pour les fonctions natives comme le fameux instanceof ou les opérateurs de casting.
Cependant, la généricité rend le code de meilleure qualité et facilite la réutilisabilité.
Code exemple
Interface IBoiteMere :
package fr.numerosoft.genericite;
public interface IBoiteMere {
// liste des méthodes à implémenter
int getCapaciteMax();
String getNomBoite();
double getPoids();
int getTaille();
String getMatiere();
Personne getProprietaire();
}
Classe Boite :
package fr.numerosoft.genericite;
import java.util.ArrayList;
/**
* Classe Boite<T> Générique qui implémente l'interface IBoiteMere
* @author phil
*
* @param <T> pour Type de Boite. Le T est uniquement valable dans la définition de la classe
*/
public class Boite<T> implements IBoiteMere {
/*
* pour y stocker ultérieurement des objets.
* Il faudra faire attention de ne pas mettre
* du fromage dans notre boite à crayons
*/
private ArrayList<T> objets;
private int capaciteMax;
private Personne proprietaire;
private String nomBoite;
private int taille;
private double poids;
private String matiere;
public Boite() {
super();
}
public Boite( int capaciteMax,
String nomBoite,
double poids,
int taille,
String matiere,
Personne proprietaire) {
super();
this.capaciteMax = capaciteMax;
this.nomBoite = nomBoite;
this.poids = poids;
this.taille = taille;
this.matiere = matiere;
this.proprietaire = proprietaire;
}
@Override
public int getCapaciteMax() {
return this.capaciteMax;
}
@Override
public String getNomBoite() {
return this.nomBoite;
}
@Override
public double getPoids() {
return this.poids;
}
@Override
public int getTaille() {
return this.taille;
}
@Override
public String getMatiere() {
return null;
}
@Override
public Personne getProprietaire() {
return null;
}
}
Classe Lancement :
package fr.numerosoft.genericite;
import java.util.HashMap;
public class Lancement {
public static void main(String[] args) {
// Mes Boites qui sont toutes de type BoiteMere gra^ce à l'implémentation par la classe
// Boite de l'interface IBoiteMere
HashMap<String, IBoiteMere> boites = new HashMap<>();
// instanciation
Boite<Pile> boitePiles = new Boite<Pile>(10, "Boîte à piles", 20, 2, "plastique", new Personne("Elon Musk"));
Boite<Fromage> boiteFromages = new Boite<Fromage>(5, "Boîte à fromages", 25, 5, "bois", new Personne("Jeanne Bonjour"));
Boite<Crayon> boiteCrayons = new Boite<Crayon>(20, "Boîte à crayons", 15, 1, "carton", new Personne("Jimmy Hendrix"));
Boite<Jouet> boiteJouets = new Boite<Jouet>(15, "Boîte à jouets", 30, 3, "plastique", new Personne("Aretha Franklin"));
// ajout dans la HashMap
boites.put("piles", boitePiles);
boites.put("fromages", boiteFromages);
boites.put("crayons", boiteCrayons);
boites.put("jouets", boiteJouets);
for (String key : boites.keySet())
{
// on récupère notre objet Objet grâce à sa clef (key)
IBoiteMere boite = boites.get(key);
System.out.println(boite.getNomBoite());
}
}
}
Liste des classes utilisées mais non remplies :
public class Crayon {
public Crayon() {
}
}
public class Fromage {
public Fromage() {
}
}
public class Jouet {
public Jouet() {
}
}
public class Pile {
public Pile() {
}
}
public class Personne {
private String nom;
public Personne() {
}
public Personne(String nom) {
super();
this.nom = nom;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
@Override
public String toString() {
return nom;
}
}