Le Tutoriel de Java Generics

View more Tutorials:

1- Pourquoi Java Generics?

Generics est un concept qui est mis en Java depuis la version 5. Avant de vous instroduire de la notion de Generics , on voit un extrait de code Java avant la version 5.
Dans cet exemple,  ArrayList est une liste dans laquelle vous pouvez ajouter, supprimer, modifier la liste et accéder aux éléments de la liste.
BeforeJ5Example.java
package org.o7planning.tutorial.generics;

import java.util.ArrayList;

public class BeforeJ5Example {

	public static void main(String[] args) {

		// Créer un objet ArrayList (une liste).
		// Pour contenir le nom de l'utilisateur
		ArrayList userNames = new ArrayList();

		// Ajouter les String à la liste.
		userNames.add("tom");
		userNames.add("jerry");

		// Vous avez accidentellement ajouté un élément non type String à la liste.
		// (Ceci est absolument permis).
		userNames.add(new Integer(100));

		// Et sortir le premier élément
		// C'est un objet (mais vous savez que c'est un String)
		// ==> tom
		Object obj1 = userNames.get(0);

		// Presser type (cast) au String
		String userName1 = (String) obj1;

		System.out.println("userName1 = " + userName1);

		// Sortir le deuxième élément
		// (vous savez que c'est String)
		// ==> jerry
		String userName2 = (String) userNames.get(1);

		System.out.println("userName2 = " + userName2);

		// Sortir le troisième élément et presser le type (cast) au String
		// (En fait, c'est un Integer).
		// (L'erreur typographique se produit ici).
		String userName3 = (String) userNames.get(2);

		System.out.println("userName3 = " + userName3);
	}

}
Une situation en Java avant la version 5 :

Vous créez un objet  ArrayList dans le but de ne contenir que des éléments avec le type de String, mais vous ajoutez à cette liste un élément qui n'est pas de type  String dans quelque part du programme (Ceci est tout à fait possible) lorsque vous sortez de cet éléments et forcez en type de String, une exception sera levée.
  • TODO (Image)
Java 5 met en concept de  Generics . Avec l'aide de  Generics , vous pouvez créer un objet  ArrayList qui ne permet que de contenir des éléments avec le type de String et ne permet pas de contenir des éléments avec d'autres types.
J5Example.java
package org.o7planning.tutorial.generics;

import java.util.ArrayList;

public class J5Example {

	public static void main(String[] args) {

		// Créer un ArrayList (Une liste)
		// Cette liste ne permet que contenir des éléments du type String.
		ArrayList<String> userNames = new ArrayList<String>();

		// Ajouter des String à la liste.
		userNames.add("tom");
		userNames.add("jerry");

		// Vous ne pouvez pas ajouter un élément qui n'est pas une String à la liste.
		// (L'erreur se produit lors de la compilation).
		userNames.add(new Integer(100)); // Compile Error!

		// Vous n'avez pas besoin de forcer l'élément sur le type (cast).
		String userName1 = userNames.get(0);

		System.out.println("userName1 = " + userName1);

	}

}
Lorsque vous créez un objet ArrayList<String> qui ne contient que des éléments de type String, le compilateur Java ne permet pas à cet objet de contenir d'autres éléments de type String.

2- Type Generic pour Class & Interface

2.1- Generics Class

L'exemple ci-dessous définit une classe generics .  KeyValue est une classe génériques qui contient une paire clé / valeur (clé/valeur).
KeyValue.java
package org.o7planning.tutorial.generics.ci;

public class KeyValue<K, V> {

   private K key;
   private V value;

   public KeyValue(K key, V value) {
       this.key = key;
       this.value = value;
   }

   public K getKey() {
       return key;
   }

   public void setKey(K key) {
       this.key = key;
   }

   public V getValue() {
       return value;
   }

   public void setValue(V value) {
       this.value = value;
   }

}
K, V dans la classe KeyValue <K, V> est appelé paramètre générique qui est un certain type de référence. Lorsque vous utilisez cette classe, vous devez déterminer le paramètre spécifique.

Prennez l'exemple qui utilise la classe  KeyValue.

KeyValueDemo.java
package org.o7planning.tutorial.generics.ci;

public class KeyValueDemo {

	public static void main(String[] args) {

		// Créer l'object KeyValue
		// Integer : Le numéro de téléphone (K = Integer)
		// String : Le nom de l'utilisateur. (V = String).
		KeyValue<Integer, String> entry = new KeyValue<Integer, String>(12000111, "Tom");

		// Java comprend le type de retour qui est Integer
		// (K = Integer).
		// ​​​​​​​
		Integer phone = entry.getKey();

		// Java comprend le type de retour qui est String
		// ​​​​​​​(V = String).
		String name = entry.getValue();

		System.out.println("Phone = " + phone + " / name = " + name);
	}

}
Exécutez l'exemple :

2.2- Héritage de la Classe Generics

Une classe étendue à partir d'une classe générique peut spécifier le type de paramètre des génériques, peut conserver les paramètres génériques ou ajouter des paramètres génériques.
Exemple 1 :
PhoneNameEntry.java
package org.o7planning.tutorial.generics.ci;

// Cette classe étend (extends) de la classe KeyValue==<==K,V>.
// Et spécifie uniquement K,V :
// K = Integer  (Le numéro de téléphone).
// V = String   (Le nom de l'utilisateur).
public class PhoneNameEntry extends KeyValue<Integer, String> {

	public PhoneNameEntry(Integer key, String value) {
		super(key, value);
	}

}
L'exemple utilise  PhoneNameEntry :
PhoneNameEntryDemo.java
package org.o7planning.tutorial.generics.ci;

public class PhoneNameEntryDemo {

	public static void main(String[] args) {

		PhoneNameEntry entry = new PhoneNameEntry(12000111, "Tom");

		// Java comprend que le type de retour qui est Integer.
		Integer phone = entry.getKey();

		// Java comprend que le type de retour qui est String.
		String name = entry.getValue();

		System.out.println("Phone = " + phone + " / name = " + name);

	}

}
Exemple 2 :
StringAndValueEntry.java
package org.o7planning.tutorial.generics.ci;

// Cette classe étend (extends) de classe KeyValue==<==K,V>.
// Spécifie le paramètre de type <K> est String.
// Garde toujours le type de paramètre Generic <V>.
public class StringAndValueEntry<V> extends KeyValue<String, V> {

	public StringAndValueEntry(String key, V value) {
		super(key, value);
	}

}
L'exemple utilise la classe  StringAndValueEntry :
StringAndValueEntryDemo.java
package org.o7planning.tutorial.generics.ci;

public class StringAndValueEntryDemo {

	public static void main(String[] args) {

		// (Le code de l'employé, le nom de l'employé).
		// V = String (Nom de l'employé)
		StringAndValueEntry<String> entry = new StringAndValueEntry<String>("E001", "Tom");

		String empNumber = entry.getKey();

		String empName = entry.getValue();

		System.out.println("Emp Number = " + empNumber);
		System.out.println("Emp Name = " + empName);

	}

}
Exemple 3 :
KeyValueInfo.java
package org.o7planning.tutorial.generics.ci;

// Cette classe s'étend (extends) de la classe KeyValue <K, V>
// Il ajoute un paramètre Generics <I>.
public class KeyValueInfo<K, V, I> extends KeyValue<K, V> {

	private I info;

	public KeyValueInfo(K key, V value) {
		super(key, value);
	}

	public KeyValueInfo(K key, V value, I info) {
		super(key, value);
		this.info = info;
	}

	public I getInfo() {
		return info;
	}

	public void setInfo(I info) {
		this.info = info;
	}

}

2.3- Generics Interface

Une Interface a des paramètres Générics :
GenericInterface.java
package org.o7planning.tutorial.generics.ci;

public interface GenericInterface<G> {

 
  public G doSomething();
 
}
Par exemple, une classe met en œuvre sur l'interface au-dessous :
GenericInterfaceImpl.java
package org.o7planning.tutorial.generics.ci;

public class GenericInterfaceImpl<G> implements GenericInterface<G>{

   private G something;
   
   @Override
   public G doSomething() {
       return something;
   }

}

2.4- Java ne soutient pas Generic Throwable

Vous ne pouvez pas créer une classe générique qui est descendante de  Throwable , car java ne soutient pas de la création de telle classe.
Le message d'erreur du compilateur :
- The generic class MyException<E> may not subclass java.lang.Throwable
Java ne soutient pas de la création d'une classe générique Throwable car elle n'apporte aucun avantage. La raison en est que l'information Generic n'est utilisée qu'au compilateur de contrôleur de code des programmeurs. Dans l'exécution de Java, les informations génériques n'existent pas, un objet de Mistake <Account> ou Mistake <User> est un type d'objet de  Mistake.
} catch( Mistake<Account> ea) {
    // Si l'exception Mistake se produit, ce bloc sera exécuté.
    ...
} catch( Mistake<User> eu) {
     // ​​​​​​​
     // Ce bloc n'est jamais exécuté
    ...
}

3- Méthode generics

Une méthode dans la classe ou de l'interface peut être produits génériques (generify )
MyUtils.java
package org.o7planning.tutorial.generics.m;

import java.util.ArrayList;

import org.o7planning.tutorial.generics.ci.KeyValue;

public class MyUtils {

	// <K,V> : Pour dire que cette méthode a deux paramètres K, V
	// La méthode renvoie un objet du type K.
	public static <K, V> K getKey(KeyValue<K, V> entry) {
		K key = entry.getKey();
		return key;
	}

	// <K,V> : Pour dire que cette méthode a deux paramètres K, V
	// La méthode renvoie un objet de type V.
	public static <K, V> V getValue(KeyValue<K, V> entry) {
		V value = entry.getValue();
		return value;
	}

	// ArrayList<E>: La liste contient les éléments du type E.
	// La méthode renvoie un objet du type E.
	public static <E> E getFirstElement(ArrayList<E> list) {
		if (list == null || list.isEmpty()) {
			return null;
		}
		E first = list.get(0);
		return first;
	}

}
Par exemple, l'utilisation de la méthode generics :
MyUtilsDemo.java
package org.o7planning.tutorial.generics.m;

import java.util.ArrayList;

import org.o7planning.tutorial.generics.ci.KeyValue;

public class MyUtilsDemo {

	public static void main(String[] args) {

		// K = Integer: Phone
		// V = String: Name
		KeyValue<Integer, String> entry1 = new KeyValue<Integer, String>(12000111, "Tom");
		KeyValue<Integer, String> entry2 = new KeyValue<Integer, String>(12000112, "Jerry");

		// (K = Integer).
		Integer phone = MyUtils.getKey(entry1);
		System.out.println("Phone = " + phone);

		// Une liste contient les éléments de type KeyValue<Integer,String>.
		ArrayList<KeyValue<Integer, String>> list = new ArrayList<KeyValue<Integer, String>>();

		// Ajouter l'élément à la liste.
		list.add(entry1);
		list.add(entry2);

		KeyValue<Integer, String> firstEntry = MyUtils.getFirstElement(list);

		System.out.println("Value = " + firstEntry.getValue());
	}

}

4- Initialisation d'objet Generic

Parfois, vous voulez initialiser un objet Généric :
// Créer un objet Generic.
T t = new T(); // Error
L'initialisation d'un objet générique comme au-dessus n'est pas autorisée, car <T> n'existe pas au moment d'exécution de Java. C'est-à-dire simplement avec le compileur de contrôle du code des programmateurs. Tous les types <T> sont les mêmes, il est compris comme  Objet lors de l'exécution de Java.

Si vous voulez initialiser l'objet Generic, vous devez fournir au Java un object Class<T> qui permet à Java de créer un objet généric lors de l'exécution en Java Reflection.
Bar.java
package org.o7planning.tutorial.generics.o;

import java.util.Date;

public class Bar {

	// Cette classe doit avoir un Constructor par défaut.
	public Bar() {

	}

	public void currentDate() {
		System.out.println("Now is: " + new Date());
	}

}
MyGeneric.java
package org.o7planning.tutorial.generics.o;

public class MyGeneric<T> {

   private T tobject;

   public MyGeneric(Class<T> tclass)
           throws InstantiationException, IllegalAccessException {
       
       this.tobject = (T) tclass.newInstance();
       
   }

   public T getTObject() {
       return this.tobject;
   }
}
MyGenericDemo.java
package org.o7planning.tutorial.generics.o;

public class MyGenericDemo {

   public static void main(String[] args) throws Exception {

       MyGeneric<Bar> mg = new MyGeneric<Bar>(Bar.class);

       Bar bar = mg.getTObject();

       bar.currentDate();
   }
}

5- Tableau Generic

Vous pouvez déclarer un tableau générique, mais vous ne pouvez pas initialiser un tableau générique.
// Vous pouvez déclarer un tableau generic.
T[] myarray;

// Mais vous ne pouvez pas initialiser un tableau generic.
// (Ceci n'est pas autorisé).
T[] myarray = new T[5];  // Error!
Exemple :
GenericArray.java
package org.o7planning.tutorial.generics.a;

public class GenericArray<T> {

	private T[] array;

	// Contructor.
	public GenericArray(T[] array) {
		this.array = array;
	}

	public T[] getArray() {
		return array;
	}

	// Renvoyer le dernier élément du tableau.
	public T getLastElement() {
		if (this.array == null || this.array.length == 0) {
			return null;
		}
		return this.array[this.array.length - 1];
	}

}
GenericArrayDemo.java
package org.o7planning.tutorial.generics.a;

public class GenericArrayDemo {

	public static void main(String[] args) {

		// Un tableau des String.
		String[] names = new String[] { "Tom", "Jerry" };

		GenericArray<String> gArray = new GenericArray<String>(names);

		String last = gArray.getLastElement();
		
		System.out.println("Last Element = " + last);
	}

}
Revenir à la question de pourquoi Java ne soutient pas d'initialiser un tableau Generic :
// Pourquoi Java ne soutient pas d'initialiser le tableau de Generic?
T[] genericArray = new T[10]; // Error!
La raison est que le type générique n'existe pas au moment de l'exécution. Liste <Chaîne> ou Liste <Entier> sont Liste. Générique ne fonctionne qu'avec dees compilateurs pour vérifier le code des programmeurs. Cela signifie que le compilateur de Java doit savoir ce qui est <T> pour compiler le new T [10] ;. Sans savoir qu'il considérera T comme Objet par défaut. Alors  : 
// Supposons que Java autorise l'initialisation d'un tableau Generic :
T[]  tarray = new T[10];

// Au moment de la compilation (Compile-time)
// le compilateur considérera <T> comme Object.
// La commande au-dessus est équivalente à :
T[] tarray  = new Object[10];

// Si au moment de l'exécution de l'application, vous spécifiez <T> comme chaîne.
// Ceci signifie :
String[] tarray = new Object[10];

// Ce qui précédant n'est pas autorisé. La raison :
// Type mismatch: ne convertit de Object[] to String[]
Si vous souhaitez initialiser un tableau générique, vous devez passer l'objet  Class <T> à Java qui permet à Java de créer un tableau générique au moment de l'exécution en utilisant Java Reflection . Voyez des exemples illustrés :
GArray.java
package org.o7planning.tutorial.generics.a;

import java.lang.reflect.Array;

public class GArray<T> {

  private Class<T> tclass;

  private T[] myArray;

  public GArray(Class<T> tclass) {
      this.tclass = tclass;

      final int size = 10;
      myArray = (T[]) Array.newInstance(tclass, size);
  }

  public T[] getMyArray() {
      return this.myArray;
  }

}
GArrayDemo.java
package org.o7planning.tutorial.generics.a;

public class GArrayDemo {

   public static void main(String[] args) {

       GArray<Integer> garray = new GArray<Integer>(Integer.class);

       Integer[] myArray = garray.getMyArray();

       myArray[0] = 1;
       myArray[2] = 0;
   }

}

6- Generics avec Wildcards

Dans le code générique, le point d'interrogation (?), appelé un représentant (wildcard ),  il représente un type inconnu. Un type de représentation paramétrique (wildcard parameterized type) est un cas du type Generic, où au moins un type de paramètre est wildcard.
L'exemple de paramètres représentatifs (wildcard parameterized) est :
  • Collection<?>
  • List<? extends Number>
  • Comparator<? super String>
  • Pair<String,?>.
Les caractères représentatives peuvent être utilisés dans diverses situations : Comme type de paramètre, de champ ou de variable locale; Parfois comme un type de retour (bien qu'il soit une meilleure pratique de programmation plus spécifique). Le caractère générique n'est jamais utilisé comme argument de type pour une invocation de méthode générique, une création d'instance de classe générique ou un supertype .
Les caractères génériques sont situés dans des positions différentes ont des significations différentes :
  • Collection <?> Décrit un ensemble qui accepte tous les types d'arguments (contient tous les types d'objets).
  • ​​​​​​​List<? extends Number> décrit une liste où tous les éléments sont du type Number ou sous-type de Number.
  • Comparator<? super String> décrit un comparateur (Comparator) dont le paramètre doit être String ou String parent.
Un type de caractère représentatif n'est pas un type spécifique pour pouvoir apparaître dans un opérateur new. Il est suggéré que les règles appliquées par les génériques java avec lequel des types sont validés dans tous les scénarios particuliers où les caractères représentatifs ont été utilisés.
  • Exemple :
Collection<?> coll = new ArrayList<String>();

// Un ensemble contient uniquement le type Number ou sous-type de Number
List<? extends Number> list = new ArrayList<Long>();

// ​​​​​​​
// Un objet possède des paramètres de type représentent.
// (A wildcard parameterized type)
Pair<String,?> pair = new Pair<String,Integer>();
Certaines déclarations sont invalides.
// String n'est pas le sous-type de Number, donc il y a une erreur.
List<? extends Number> list = new ArrayList<String>();  

// String n'est pas un type parent d'Integer, donc il y a une erreur.
ArrayList<? super String> cmp = new ArrayList<Integer>();

6.1- L'exemple avec le type représentatif (wildcard)

WildCardExample1.java
package org.o7planning.tutorial.generics.w;

import java.util.ArrayList;

public class WildCardExample1 {

	public static void main(String[] args) {

		// Une liste contient les éléments de type String.
		ArrayList<String> listString = new ArrayList<String>();

		listString.add("Tom");
		listString.add("Jerry");

		// Une liste contient les éléments de type Integer
		ArrayList<Integer> listInteger = new ArrayList<Integer>();

		listInteger.add(100);

		// Vous ne pouvez pas déclarer:
		ArrayList<Object> list1 = listString; // ==> Error!

		// Un objet avec type paramètre représentant
		// (wildcard parameterized object).
		ArrayList<? extends Object> list2;

		// Vous pouvez déclarer:
		list2 = listString;

		// Ou
		list2 = listInteger;

	}

}
WildCardExample2.java
package org.o7planning.tutorial.generics.w;

import java.util.ArrayList;
import java.util.List;

public class WildCardExample2 {

   public static void printElement(List<?> list) {
       for (Object e : list) {
           System.out.println(e);
       }
   }

   public static void main(String[] args) {

       List<String> names = new ArrayList<String>();
       names.add("Tom");
       names.add("Jerry");
       names.add("Donald");

       List<Integer> values = new ArrayList<Integer>();

       values.add(100);
       values.add(120);

       System.out.println("--- Names --");

       printElement(names);

       System.out.println("-- Values --");

       printElement(values);

   }

}

6.2- L'objet représentatif ne peut pas utiliser la méthode Generic

ValidWildcard1.java
package org.o7planning.tutorial.generics.w;

import java.util.ArrayList;

public class ValidWildcard1 {

	public static void main(String[] args) {

		// Une liste contient les éléments de type String.
		ArrayList<String> listString = new ArrayList<String>();

		// Utilisation de la méthode generic: add(E).
		// Ajouter un élément différent de null à la liste
		listString.add("Tom");

		listString.add("Jerry");

		// Ajouter un élément null à la liste.
		listString.add(null);
	}

}
InvalidWildcard1.java
package org.o7planning.tutorial.generics.w;

import java.util.ArrayList;

public class InvalidWildcard1 {

	public static void main(String[] args) {
 
		// Une liste avec le type du paramètre représentant
		// (wildcard parameterized type).
		ArrayList<? extends Object> listWildcard = listString;

		// Vous ne pouvez pas utiliser la méthode add(E)
		// avec les paramètres qui ont la valeur différente null
		listWildcard.add("Tom"); // ==> Error!

		listWildcard.add("Jerry"); // ==> Error!

		// Ajouter un élément null à la liste.
		listWildcard.add(null);

	}

}

6.3- Wildcard ne peut pas participer à l'opérateur new

Un type de paramètre générique (wildcard parameterized type) n'est pas un type spécifique et il ne peut pas apparaître dans un opérateur ​​​​​​​new.
// Paramètre Wildcard ne peut pas participer à l'opérateur new
List<? extends Object> list= new ArrayList<? extends Object>();

View more Tutorials: