Les generics en Java : comment arrêter d'avoir peur des chevrons et commencer à écrire du code fiable
\n\nDans le monde de Java, il y a une chose qui effraie les débutants presque autant que le multithreading : ce sont les chevrons <>. Les Generics (aussi appelés génériques ou types génériques) semblent mystérieux, mais en réalité, ce sont vos meilleurs alliés pour un code propre et sûr. Décortiquons ensemble comment fonctionne le typage en Java avec des exemples que vous pourrez appliquer dès aujourd'hui.
Imaginez : vous écrivez une méthode qui doit fonctionner à la fois avec des nombres, des chaînes de caractères et vos propres classes. Sans les generics, il faudrait multiplier les méthodes surchargées ou utiliser Object et vérifier les types manuellement en permanence. Les types génériques Java résolvent ce problème de manière élégante et sûre.
Pourquoi sans les generics le code devient-il un champ de mines ?
\n\nRappelons-nous comment les programmeurs vivaient avant l'arrivée des generics (avant Java 5). Si vous avez déjà écrit un ArrayList sans spécifier le type, vous connaissez ce cauchemar :
List list = new ArrayList();\nlist.add("Bonjour");\nlist.add(42);\nlist.add(new Object());\n\n// Quelque part 100 lignes plus loin :\nString text = (String) list.get(1); // ClassCastException à l'exécution !\n\nLe problème classique : ClassCastException — une erreur qui ne se manifeste qu'à l'exécution. Vous auriez pu passer la liste à une autre méthode, où quelqu'un est convaincu qu'elle ne contient que des chaînes. Et boum ! L'application plante. Les generics en Java déplacent la vérification des types à l'étape de compilation — c'est comme une assurance qui se déclenche avant que vous ne preniez le volant.
\n\nLes generics en Java : la syntaxe de base à connaître
\n\nÀ la base des generics se trouve une idée simple : nous paramétrons une classe ou une méthode avec un type. Au lieu d'un type concret, nous écrivons un paramètre de type (généralement une lettre majuscule : T, E, K, V).
L'exemple le plus populaire — les collections Java :
\n\n// Sans generic — dangereux\nList rawList = new ArrayList();\n\n// Avec generic — sûr\nList<String> safeList = new ArrayList<>();\nsafeList.add("Java");\nsafeList.add("Generics");\n// safeList.add(123); // Erreur de compilation !\n\nLe compilateur sait désormais : à l'intérieur de safeList, il ne peut y avoir que des chaînes. Tenter d'ajouter un nombre provoquera une erreur à la compilation, et non à l'exécution. C'est cela, la sécurité des types Java en action.
Créons notre première classe générique
\n\nSupposons que nous écrivions un simple conteneur pour stocker une seule valeur. Sans les generics, il faudrait utiliser Object :
public class Box {\n private Object value;\n\n public void set(Object value) { this.value = value; }\n public Object get() { return value; }\n}\n\nEt maintenant, avec un generic :
\n\npublic class Box<T> {\n private T value;\n\n public void set(T value) { this.value = value; }\n public T get() { return value; }\n}\n\n// Utilisation :\nBox<Integer> intBox = new Box<>();\nintBox.set(100);\nint value = intBox.get(); // Sans cast !\n\nRemarquez : nous n'avons plus besoin d'écrire (Integer) intBox.get(). Le compilateur sait lui-même que get() retournera un Integer. C'est la magie des types génériques Java.
Typage en Java : contraintes et wildcards
\n\nLes generics seraient trop simples s'il n'y avait pas de nuances. La principale concerne les bounded type parameters (paramètres de type bornés). Parfois, il faut dire : "Le type T doit être un nombre" ou "T doit implémenter l'interface Comparable".
\n\n// T ne peut être que Number ou une sous-classe de Number\npublic class Calculator<T extends Number> {\n private T value;\n\n public double square() {\n return value.doub