précédent | suivant | table des matières s'évaluer

généricité

Sommaire
  1. Pourquoi la généricité ?
  2. Méthode générique
  3. Limite pour les type paramètre
  4. Effacement
  5. Généricité et héritage
  6. Joker
  7. Capture du joker
  8. Généricité et reflexion
  9. Méthodes pont

La généricité permet de définir des classes, et des méthodes paramétrées par une ou plusieurs classes.

1 Pourquoi la généricité ?

Exemple : Soit la classe suivante :


public class Paire {
   Object premier ;
   Object second;
   public Paire (Object a, Object b){
      premier= a; second = b; 
   } 
   
   public object getPremier(){ 
     return premier;
   } 
   
   public object getSecond(){ 
      return second;
   } 

Les deux inconvénients sont :


Paire  p = new Paire ("abc", "xyz");
String x =  (String)p.getPremier(); // le casting est obligatoire
Double y =  (Double)p.getSecond();  // Il faut attendre l'exécution pour avoir 
				// une levée d'exception(ClassCastException)

On définit alors une classe paramétrée :

         
public class Paire<T> {  
   T premier ;
   T second;
   public Paire (T a, T b){
      premier a; second = b;  
   } 
   public T getPremier(){ 
      return  premier; 
   }
   
   public getSecond(){ 
      return second;
   } 
}

Le programme est alors plus simple et plus sur :

   
  Paire<String>  p = new Paire<String>  ("abc", "xyz");
  String x =  p.getPremier(); // pas de cast
  Double y =  p.getSecond();  // erreur de compilation (type mismatch)

2 Méthode générique

On peut définir une méthode générique dans une classe de la façon suivante :

     
public class X {
   public <T>  void affiche(Paire<T> p){
      System.out.println(p);
   }
   
   public <T> T choix(T a, T b){
      return (int)(Math.random()*2)==1?a:b;
   }
   
   public static void main(String []a){ 
      Paire<String> ps = new  Paire<String>("un", "deux");   
      Paire<Integer> pi = new Paire<Integer>(1, 2);
      X x = new X(); 
      x.affiche(ps);
      x.affiche(pi);
      Number n = x.choix(new Integer(2), new Double(3.14159));
   }
}

Dans la dernière ligne, le compilateur fait une inférence de type, et calcule la première super classe commune aux deux classes Integer et Double, soit Number.

3 Limite pour les types paramètre

Supposons que nous voulions ajouter à la classe Paire<T> la méthode suivante :

   
public T min(){
   if(premier.compareTo(second)<=0) return premier;
   else return second;
}
Le compilateur signale alors que la méthode compareTo n'est pas définie pour le type T. Il faut restreindre T à une classe qui a cette méthode, et définir la classe Paire de la façon suivante :
         
public class Paire<T extends Comparable> {  
   T premier ;
   T second;
   public Paire (T a, T b){
      premier a; second = b;  
   } 
   public T getPremier(){ 
      return  premier; 
   }
   
   public getSecond(){ 
      return second;
   }

   public T min(){ 
      if(premier.compareTo(second)<=0) return premier;
      else return second;
   } 
}
Le type limitant peut être une classe ou une interface. On définira :
public class Paire<T extends Number> { ... }
pour définir une paire de nombres... et
public class Paire<T extends A & Comparable> {  ... }
pour définir une paire d'élément de la classe A ou de classes dérivées de A et qui sont Comparable.

4 Effacement

Les classes paramétrées sont compilées vers une classe représentant le type brut : le type équivalent débarrassé des paramètres de la classe. On dit que les paramètres de type sont effacés. Ceci a des conséquences :


5 Généricité et héritage

Soient les deux classes suivantes :

public  class Super {
	   ...
 }
public class  Sous extends Super {
           ...
 }

Il n'existe pas de relation d'héritage entre Paire<Super> et Paire<Sous>.

  
   Paire<Super> pSup  = new Paire<Super>(new Super(), new Super());
   Paire<Sous>  pSous = new Paire<Sous>(new Sous(), new Sous()); 
   pSup = pSous ; // INTERDIT

Supposons que celà soit possible,nous pourrions alors écrire :

  pSup.setPremier(new Super());

Or pSup et pSous désigne le même objet et l'objet désigné par pSous aurait son attribut premier de type Super !!!


6 Joker

Supposons que nous définissions dans la classe X une autre méthode pour afficher des paires de Super, de la façon suivante :

public void afficheS(Paire<Super>p){
         System.out.println(p);
 }

 Paire <Super>  ps = new Paire<Super>();
 Paire <Sous> pso =  new Paire< Sous>();

        
x.afficheS(ps);
OK, pas de problème.
x.afficheS(pso);
NON, pso n’est pas une Paire de Super

Pour que la dernière instruction soit possible il faudrait définir la méthode afficheS de la façon suivante : 

public void afficheS(Paire<?> p){
    System.out.println(p);
 }
Mais dans ce cas tout est possible, et si on veut se limiter aux classes qui dérivent de Super :
public void  afficheS(Paire<? extends Super>p){ 
     System.out.println(p);
 }

Les jokers peuvent être limités vers le haut (extends) ou vers le bas (super). 

On a les relations d'héritage suivantes :

généricité1.gif
généricité 2 .gif

La relation d'héritage entre Paire et Paire<?> est là pour assurer la compatibilité avec les codes existants avant la version 5 de Java.

7 Capture du Joker

Soit la classe :

         
public class Paire<T> {  
   T premier ;
   T second;
   public Paire (T a, T b){
      premier a; second = b;  
   } 
   public T getPremier(){ 
      return  premier; 
   }
   
   public getSecond(){ 
      return second;
   } 
   
   public void setPremier(T t){
      premier = t;
   }
}
On a alors :
         
   Paire<?>                p1 = new Paire<Integer>( 1, 2);
   Paire<? extends Number> p2 = new Paire<Integer>( 1, 2);

   Object  o = p1.getPremier(); // OK
   Number  n = p1.getPremier(); // ERREUR non concordance de types : 
                                // impossible de convertir de capture-of ? en Number
   Integer i = p1.getPremier(); // ERREUR non concordance de types : 
                                // impossible de convertir de capture-of ? en Integer

           o = p2.getPremier(); // OK
           n = p2.getPremier(); // OK
           i = p2.getPremier(); // ERREUR non concordance de types : 
                                // impossible de convertir de capture-of ? extends Number en Integer

Le compilateur désigne par capture-of ? le type inconnu qui paramètre la paire p1, le compilateur désigne par capture-of ? extends Number le type inconnu qui paramètre la paire p2.

8 Généricité et réflexion

On peut savoir si une classe est une classe générique (informations conservées à la compilation), mais on ne peut évidemment pas connaître l'instanciation précise d'un type.
exemple :

public  String toString(Class c){
   StringBuffer res = new StringBuffer("");
   TypeVariable[] tv = c.getTypeParameters();
   res.append(c.getName());
   if(tv.length!=0){
      res.append("<");
      for (int i = 0; i < tv.length-1; ++i)
         res.append(tv[i]+", ");
      res.append(tv[tv.length-1]);
      res.append(">");
   }
   return res.toString();
}
L'usage de cette méthode pour une classe c permet d'obtenir le nom de la classe suivi de ses paramètres si la classe est générique.

9 Méthodes Pont

Soit les définitions suivantes :

public interface Comparable <T>{
   public int compareTo(T t);
}
public class X implements Comparable<X> {
   public int compareTo(X t) {
     return 0;
   }
}

L'effacement dans l'interface Comparable génère une méthode int compareTo(Object t), et la classe X définit la méthode int compareTo(X t). La classe X ne redéfinit donc pas int compareTo(Object t) : le compilateur ajoute la méthode pont :


public class X implements Comparable<X> {
   public int compareTo(Object t) {
     return compareTo((X)t);
   }
   public int compareTo(X t) {
     return 0;
   }
}

haut de la page