UP | HOME

Paramétrer par des types - La généricité

Table of Contents

Session 4 - Travaux dirigés - 15 juin 2015 - 16h30-17h45 - 1h15'

Les exercices qui suivent traitent de la généricité, c'est-à-dire de la paramétrisation par des types. Les deux premiers comparent la situation avec la généricité à celle sans. Le dernier approfondit la notion de quantification, au cœur de la généricité. Pour tous ces exercices, utiliser Eclipse pour programmer et consulter la documentation de l'API en ligne. Attention : vous devez vous assurer que vous utilisez bien un compilateur pour la version 5 ou ultérieure de Java (pour le projet utilisé, cf. File > Properties > Java Compiler). L'archive des corrections reprend les différentes exercices. Elle contient trois paquets sous td4, un par exercice, contenant chacun une unité de compilation (un fichier). Vous y trouverez un squelette du code à compléter.

L'API Java

On traitera l'exemple des listes définies dans le paquet java.util de l'API ("Application Programming Interface") de Java. Vous trouverez la documentation sur le Web.

Version 4 (appelée 1.4) – Sans généricité

  • La documentation n'est plus en ligne.

Versions 5 à 8 – Avec généricité

Remarque : dans la suite, l'expression représente du code supposé connu.

Interfaces et classes génériques

Comment définir des interfaces et des classes génériques ? Quelle est leur utilité ?

Définition

Comment l'interface List est-elle définie ? La classe d'implémentation LinkedList ? Comparer la version générique à celle non générique. Compléter ces extraits de code provenant de l'API.

// A compléter en suivant la documentation en ligne.
// On notera X5 le type X de la version au moins 5
//   et X4 le type X de la version 4.

/* Java générique */
// Pour tout type E, l'interface List5 de E est égale à ...
interface List5 .................................................... ...
{ 
  boolean add(........................................................); 
  void add(int index, ................................................); 
  ............. get(int index); 
  ...
}    
// Pour tout type E, la classe LinkedList de E 
//   implémentant List de E est égale à ... 
class LinkedList5 ......................................................
{
  ...
}

/* Java non générique */
// Impossible de paramétrer par des types. Utiliser le polymorphisme.
interface List4 ...
{
  boolean add(........................................................); 
  void add(int index, ................................................); 
  ......................... get(int index); 
  ...
}
class LinkedList4 ......................................................
{
  ...
}

Utilisation

Implémenter la fonction principale en suivant les commentaires. Comparer la version générique à celle non générique.

interface Nat {
  int val();
}
class NatParInt implements Nat {
  private int val;
  public NatParInt(int x) {
    this.val = x;
  }
  public int val() {
    return this.val;
  }
}
// Compléter suivant les commentaires.
// Utiliser les listes du paquet java.util. Un type générique
// peut être utilisé à l'ancienne sans paramètre de types.
  public static void main(String[] args) {
    /* Java générique */

    //Créer une liste chaînée vide d'entiers naturels.
    ....................................................................

    // Ajouter à (la fin de) cette liste trois entiers naturels.
    ....................................................................
    ....................................................................
    ....................................................................

    //Récupérer le second entier de la liste.
    ....................................................................

    // Appeler la méthode *val* de cet entier.
    ....................................................................

    /* Java non générique */

    //Créer une liste chaînée vide d'entiers naturels.
    ....................................................................

    // Ajouter à (la fin de) cette liste trois entiers naturels.
    ....................................................................
    ....................................................................
    ....................................................................

    //Récupérer le second entier de la liste.
    ....................................................................

    // Appeler la méthode *val* de cet entier.
    ....................................................................
  }

Calculs génériques

Comment définir des fonctions et des méthodes génériques ? Quelle est leur utilité ?

Définition

Comment la fonction Collections.nCopies de l'API est-elle déclarée et définie ? Comment la méthode List.addAll de l'API est-elle déclarée ? Comparer la version générique à celle non générique.

// A compléter en suivant la documentation en ligne.
// On notera X5 le type X de la version au moins 5
//   et X4 le type X de la version 4.

/* Java générique */
class Collections5 {
  ...
  // Pour tout type T, la fonction nCopies dépendant de T 
  //   est définie par ...
  public static ..................................... // Pour tout type T
   List5............. nCopies(int n, .................................) {
    // (En première approximation)
    List5<T> r = new LinkedList5<T>();
    for(int i = 0; i < n; i++){
      r.add(o);
    }
    return r;
  }
}
interface List5.................................................... ...{
  ... 
  // Quantification existentielle bornée pour le type du paramètre c.
  boolean addAll(.....................................................);
  // Equivalence avec la quantification universelle bornée
  //   pour la déclaration entière.
  .......................... boolean addAll(...........................);
}
/* Java non générique */
class Collections4 {
  ...
  public static List4 nCopies(int n, ...........................) { ... }
}
interface List4 ... {
  ...
  boolean addAll(.......................................................);
}

Utilisation

Implémenter la fonction principale en suivant les commentaires. Expliquer l'origine des erreurs à la compilation.

On suppose que l'interface Image est un sous-type de l'interface Media, et que la classe ImagePNG implémente Image.

Comparer la version générique à celle non générique.

interface Media {}
interface Image extends Media {}
class ImagePNG implements Image{}

  public static void main(String[] args) {
    /* Version générique */
    // Créer une *Image*.
    ....................................................................

    // Définir une liste *lImages* formée de quatre copies de l'*Image* précédente et
    //   de type *List<Image>*. L'inférence de type est-elle possible ?
    ....................................................................
    ....................................................................

    // Définir une liste *lMedias* formée de quatre copies de l'*Image* précédente et
    //   de type *List<Media>*. L'inférence de type est-elle possible ? 
    ....................................................................
    ....................................................................
    .................................................................... 

    // Ajouter la liste *lImages* à *lMedias*.
    ....................................................................
    ....................................................................
    ....................................................................

    // Ajouter la liste *lMedias* à *lImages*.
    ....................................................................
    ....................................................................

    // Répéter les mêmes instructions avec un *Media*, obtenu à partir d'une *Image*. 

    // Créer un *Media*.
    ....................................................................
    ....................................................................

    // Réinitialiser la liste *lImages* pour qu'elle soit formée de quatre
    //    copies du *Media* précédent.  
    ....................................................................
    ....................................................................

    // Réinitialiser la liste *lMedias* pour qu'elle soit formée de quatre
    //   copies du *Media* précédent.  L'inférence de type est-elle possible ?
    ....................................................................
    ....................................................................

    // Ajouter la liste *lImages* à *lMedias*.
    ....................................................................

    // Ajouter la liste *lMedias* à *lImages*.
    ....................................................................

    /* Version non générique */

    // Définir une liste *kImages* formée de quatre copies de l'*Image* précédente et
    //   de type *List<Image>*. 
    ....................................................................

    // Définir une liste *kMedias* formée de quatre copies de l'*Image* précédente et
    //   de type *List<Media>*.
    ....................................................................

    // Ajouter la liste *kImages* à *kMedias*.
    ....................................................................

    // Ajouter la liste *kMedias* à *kImages*.
    ....................................................................

    // Répéter les mêmes instructions avec un *Media*, obtenu à partir d'une *Image*. 

    // Réinitialiser la liste *kImages* pour qu'elle soit formée de quatre
    //    copies du *Media* précédent.  
    ....................................................................

    // Réinitialiser la liste *kMedias* pour qu'elle soit formée de quatre
    //   copies du *Media* précédent.  
    ....................................................................

    // Ajouter la liste *kImages* à *kMedias*.
    ....................................................................

    // Ajouter la liste *kMedias* à *kImages*.
    ....................................................................
  }

Quantification universelle bornée

Comment borner une quantification universelle ?

Réalisabilité

Quelles sont les fonctions réalisables par la fonction déclarée ainsi ?

static <T> T f(T x) { 
  ... 
}
static <T> T f1(T x) {
  ....................................................................
}
static <T> T f2(T x) {
  ....................................................................
}

Pour permettre de réaliser plus de fonctions, on doit donner plus d'informations concernant le type de l'argument. Voyons deux solutions.

Type générique

Une première solution est d'utiliser un type générique, comme List<T>. Quelles sont les fonctions réalisables par la fonction déclarée ainsi ?

static <T> T g(List<T> x) { 
  ... 
}

Donnez un exemple.

static <T> T g(List<T> x) { 
  .................................................................... 
  .................................................................... 
  .................................................................... 
}

Est-il possible de définir la fonction calculant le plus petit élément d'une liste ?

....................................................................... 
....................................................................... 
....................................................................... 

Majoration du type paramètre

Une seconde solution est de majorer le type paramètre. Implémenter la fonction calculant le plus petit élément d'une liste. On pourra utiliser l'interface générique Comparable<T>, ce qui permet de définir le pré-ordre par les couples (x, y) tels que x.compareTo(y) <= 0, et l'équivalence associée par les couples (x, y) tels que x.compareTo(y) == 0.

......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................

Comparer avec la fonction Collections.min de l'API.

/* Version de l'API */
static ...............................................................
T min(.............................................................) { 
  ... 
}

Last Updated 2015-06-11T11:14+0200. Comments or questions: Send a mail.