Grâce aux generics (types paramétrés ou types génériques), les instances peuvent opérer sur les types bien définis. Cela peut éviter les problèmes d'exploitation du code quand par exemple notre Map prend comme valeur un String tandis qu'elle devrait accepter uniquement les Integer. Regardons comment cette situation se présente sans (méthode without()) et avec (méthode with()) les generics.
import java.util.HashMap; import java.util.Map; import java.util.Iterator; public class Generics { public static void main(String[] args) { without(); with(); } private static void without() { Map intMap = new HashMap(); intMap.put("a", 3); intMap.put("b", 6); intMap.put("c", "ao"); parseMaps(intMap); } private static void with() { Map<String, Integer> intMap = new HashMap<String, Integer>(); intMap.put("a", 1); intMap.put("b", 2); intMap.put("c", 3); parseMaps(intMap); } private static void parseMaps(Map parsedMap) { Iterator it = parsedMap.entrySet().iterator(); while (it.hasNext()) { Map.Entry pairs = (Map.Entry) it.next(); Integer v = (Integer) pairs.getValue(); System.out.println("Short value of " +v+ " : " + v.shortValue()); } } }
On verra qu'au moment de compilation le compilateur nous affiche un avertissement :
Note: Generics.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details.
En lancant le code, on reçoit une exception suivante :
Short value of 6 : 6 Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer at Generics.parseMaps(Generics.java:34) at Generics.without(Generics.java:18) at Generics.main(Generics.java:9)
Si l'on remplace Map intMap = new HashMap(); par eeeMap<String, Integer> intMap = new HashMap<String, Integer>();, au moment de compilation on recevra un message d'erreur :
Generics.java:17: put(java.lang.String,java.lang.Integer) in java.util.Map<java.lang.String,java.lang.Integer> cannot be applied to (java.lang.String,java.lang.String) intMap.put("c", "ao"); ^ 1 error
Grâce à ce comportement, déjà à ce stade, on peut réduire le nombre de bugs potentiels. Generics facilitent également le travail en équipe. Les nouveaux développeurs qui arrivent sur un projet savent tout de suite quel type de données attend l'application.
Classes et interfaces avec genericsEgalement les classes nouvellement créées peuvent contenir les types paramétrés. Elles prennent alors comme paramètre(s) une lettre en majuscule. Cette régle a été introduite pour éviter des confusions entre un type paramétré et une classe normale.
- E : correspond à un élément. Il est utilisé pout le type des éléments d'une collection (ex : Collection
- K : correspond à la clé dans le cas d'une interface clé - valeur. Il est utilisé dans les Maps (ex : Map<K, V>)
- N : correspond au nombre.
- T : correspond au type utilisé (Integer, String etc.). Il peut être utilisé dans un formatteur (ex : Formatter
- V : correspond à la valeur dans le cas d'une interface clé - valeur. Il est utilisé dans les Maps (ex : Map<K, V>)
public class GenericsClass { public static void main(String[] args) { TestClass<Integer> tcInt = new TestClass<Integer>(30); tcInt.printValue(); TestClass<String> tcString = new TestClass<String>("test"); tcString.printValue(); BookbookString = new BookPdf<String, String>("print", "Test book name"); bookString.getKey(); Book<Integer, String> bookInteger = new BookPaper<Integer, String>(1, "Test book name II"); bookInteger.getKey(); } } /** * Exemple with T (type) */ class TestClass<T> { private Object value; public TestClass(Object v) { this.value = v; } public void printValue() { System.out.println("TestClass value is : " + value); } } /** * Exemple with key - value pair */ interface Book<K, V> { public Object getKey(); public Object getValue(); } class BookPdf<K, V> implements Book<K, V> { private Object key; private Object value; public BookPdf(Object key, Object value) { this.key = key; this.value = value; } public Object getKey() { key = (String) key; if (key.equals("print")) { System.out.println("Log : We will print your PDF book right now"); } else if (key.equals("download")) { System.out.println("Log : You can download your book"); } return key; } public Object getValue() { return value; } } class BookPaper<K, V> implements Book<K, V> { private int key; private Object value; public BookPaper(int key, Object value) { this.key = (int) key; this.value = value; } public Object getKey() { if (key == 1) { System.out.println("Log : Book will be send tomorrow"); } else { System.out.println("Log : Your book won't be send tomorrow"); } return key; } public Object getValue() { return value; } }