Blog ENI : Toute la veille numérique !
-25€ dès 75€ sur les livres en ligne, vidéos... avec le code FUSEE25. J'en profite !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Algorithmique
  3. La généricité
Extrait - Algorithmique Des bases à la programmation orientée objet en Java (avec exercices et corrigés) (2e édition)
Extraits du livre
Algorithmique Des bases à la programmation orientée objet en Java (avec exercices et corrigés) (2e édition) Revenir à la page d'achat du livre

La généricité

Présentation

La généricité consiste à créer des algorithmes qui peuvent s’appliquer à différents types de données. À la place de définir des algorithmes spécifiques pour chaque type de données, un unique algorithme est développé de manière générique. Il est ensuite possible de l’utiliser avec un type de données bien précis. 

Les procédures et les fonctions génériques

1. La déclaration

Les procédures et les fonctions peuvent être génériques. Elles sont alors paramétrées par un ou plusieurs types.

Prenons l’exemple d’une procédure affichant l’ensemble des éléments d’un tableau. Quel que soit le type des éléments contenus dans le tableau, les opérations à réaliser sont les mêmes : parcourir les différentes cases du tableau et pour chacune afficher la valeur contenue. Il serait dommage de devoir en écrire une pour afficher un tableau d’entiers, une autre pour un tableau de texte, une autre pour un tableau de dates...

Une seule procédure générique est alors écrite, paramétrée par un type (le type d’un élément du tableau). Lorsque nous avons besoin d’utiliser cette procédure pour afficher un tableau avec un type bien précis d’éléments, des entiers par exemple, le compilateur va automatiquement remplacer le type générique par le type entier.

Syntaxe pour déclarer une procédure générique :

Procédure nomDeLaProcedure<TypeGénérique>(listeDesParamètres) 

Exemple :

Procédure afficherTableau<T>(tab : T[], taille : entier)   
Variable i : entier   
Début   
  écrireSRC("[" & tab[0])   
  Pour i <- 1 à taille - 1   
    écrireSRC(", " & tab[i])   
  FPour   
  écrire("]")*  
Fin 

Le type générique est indiqué entre un couple de chevrons. Dans cet exemple, ce type a été nommé T (comme type). Il est assez fréquent de le nommer ainsi, mais vous êtes libre de le nommer comme vous le souhaitez.

Syntaxe pour déclarer une fonction générique :

Fonction nomDeLaProcedure<TypeGénérique>(listeDesParamètres)   
                               ...

Les classes génériques

1. La déclaration

Les classes peuvent également être paramétrées par un ou plusieurs types. Elles auront dans ce cas un ou plusieurs attributs de type générique.

Une classe générique ne possède pas nécessairement de méthodes génériques et une méthode générique n’est pas nécessairement déclarée au sein d’une classe générique.

Syntaxe pour déclarer une classe générique :

Classe NomClasse<TypeGénérique>  
Attribut nomAttribut : TypeGénérique 
...  
FClasse 

Exemple :

Classe Couple<U, V>   
Attribut element1 :  
Attribut element2 :  
   
Méthodes      
Constructeur(un : U, deux : V)   
Début   
  instance.element1 <- un   
  instance.element2 <- deux   
Fin   
   
Fonction getElement1() Retourne U   
Début   
  Retourner instance.element1   
Fin   
   
Fonction getElement2() Retourne  
Début   
  Retourner instance.element2   
Fin   ...

Les interfaces génériques

Les interfaces permettent de regrouper un ensemble de méthodes abstraites et éventuellement de constantes (cf. chapitre Les éléments abstraits). Les interfaces peuvent être paramétrées par un ou plusieurs types.

Une interface générique peut également imposer des contraintes sur son ou ses types génériques.

1. La déclaration

Syntaxe :

Interface NomDeLInterface<TypeGénérique>   
...   
FInterface 

Cette interface générique contiendra une ou plusieurs méthodes abstraites utilisant ce type générique.

Exemple :

Interface EnsembleTrie<T ordonné>  
 
Procédure abstraite ajouter(valeur : T)  
Procédure abstraite retirer(position : entier)  
Fonction abstraite elementEn(position : entier) Retourne 
   
FInterface 

Cette interface permet de gérer un ensemble de données. La particularité c’est que les éléments présents dans cet ensemble sont triés. Cette interface est paramétrée par le type T correspondant aux types des éléments contenus dans cet ensemble. La contrainte ordonnée a été positionnée sur ce type pour que les éléments puissent être comparés entre eux et positionnés au bon emplacement.

La première méthode abstraite prend en paramètre un élément de type T. Si un ensemble trié d’entiers est créé, ce type T sera remplacé par entier. La seconde méthode abstraite n’utilise pas le type T. Quel que soit le type générique utilisé cette méthode ne change pas. Enfin, la dernière méthode abstraite retourne une valeur de type T. Son objectif est de retourner la valeur à la position passée en paramètre. Elle retourne donc bien une valeur de type T.

2. L’implémentation

Une classe peut implémenter une ou plusieurs interfaces, qu’elles soient ou ne soient pas génériques. Lorsqu’une classe implémente une interface générique, elle a deux possibilités : soit elle indique par quoi le type générique...

La généricité en Java

Java a introduit la généricité depuis la version 5 du JDK. Il est possible de créer des méthodes génériques, des interfaces génériques ainsi que des classes génériques. De plus, dans la bibliothèque standard, des méthodes, interfaces et classes génériques sont disponibles et peuvent directement être utilisées.

La généricité est traitée au moment de la compilation. Lors de cette étape, les types utilisés sont vérifiés. Mais le code compilé est non générique car la machine virtuelle Java est restée non générique même après la version 5 de Java.

1. Les méthodes génériques

a. La déclaration

Pour définir une méthode générique, il faut ajouter le type générique entre un couple de chevrons au sein de la déclaration de la méthode juste avant le type de retour.

Syntaxe :

visibilité [static] <TypeGénérique> typeRetour 
                 nomDeLaMéthode(listeDesParamètres) { 
... 
} 

Exemple :

public static <T> void afficherTableau(T[] tab) {   
  System.out.print("[");   
  if (tab.length > 0) {   
    System.out.print(tab[0]); 
    for (int i = 1; i < tab.length; i++) { 
      System.out.print(", " + tab[i]); 
    } 
  } 
  System.out.println("]"); 
} 

b. L’appel

De manière similaire à ce qui se fait en algorithmique, il est possible d’appeler la méthode normalement et en fonction des paramètres, le type concret devant remplacer le type générique est déduit automatiquement.

Exemple :

String[] tabTexte = new String[3]; 
 
tabTexte[0] = "Algo"; 
tabTexte[1] = "Généricité"; 
tabTexte[2] = "Java"; 
   
afficherTableau(tabTexte); 

L’appel à la méthode générique...

Exercices

1. Tirage au sort

Écrire une fonction générique prenant en paramètre un tableau contenant des éléments de type générique et sa taille et retourne un des éléments du tableau choisi aléatoirement.

Réaliser un algorithme utilisant cette fonction générique pour réaliser un tirage au sort parmi un ensemble de participants.

2. À l’envers

Définir une procédure générique inversant l’ordre des éléments d’un tableau.

Faire appel à cette procédure dans un algorithme.

3. Se mélanger les pinceaux

Créer une procédure générique mélangeant les éléments d’un tableau.

Un pinceau est caractérisé par sa forme et sa taille. Créer une classe Pinceau.

Écrire un algorithme créant un tableau de pinceaux, mélangeant l’ordre des pinceaux et affichant l’ensemble des pinceaux.

4. Le plus grand

Créer une fonction générique retournant la plus grande valeur parmi trois passées en paramètre.

Réaliser un algorithme utilisant cette fonction.

5. Procédures et fonctions génériques en Java

Implémenter les exercices précédents en Java

6. Apprentissage liste de vocabulaire

Réaliser un programme en Java permettant l’apprentissage...

Correction des exercices

1. Tirage au sort

Fonction tirageAuSort<T>(tableau : T[], taille : entier) 
                                                       Retourne 
Début 
  Retourner tableau[aléa(0, taille - 1)]  
Fin 
 
Algo GagnantTireAuSort  
Constante TAILLE : entier <- 4  
Variable participants : texte[TAILLE]  
Variable gagnant : texte  
Début 
  participants[0] <- "Tom" 
  participants[1] <- "Aude" 
  participants[2] <- "Jade" 
  participants[3] <- "Félix" 
  gagnant <- tirageAuSort(participants, TAILLE) 
  écrire("Le gagnant est " & gagnant)  
Fin 

2. À l’envers

Procédure reverse<T>(tableau : T[], taille : entier)  
Variable tmp : 
Variable i : entier  
Début 
  Pour i <- 0 à taille div 2 - 1 
    tmp <- tableau[i] 
    tableau[i] <- tableau[taille - 1 - i] 
    tableau[taille - 1 - i] <- tmp  
  FPour 
Fin 

 

Algo TestReverse  
Constante TAILLE : entier <- 10  
Variable valeurs : entier[TAILLE]  
Variable i : entier  
Début 
  Pour i <- 0 à TAILLE - 1 
    valeurs[i] <- i  
  FPour 
  reverse(valeurs, TAILLE)  
  Pour i <- 0 à TAILLE - 1 
    écrire(valeurs[i])  
  FPour 
Fin 

3. Se mélanger les pinceaux

Procédure melange<T>(tableau : T[], taille : entier)  
Variable tmp : 
Variable i, posAlea1, posAlea2 : entier  
Début 
  Pour i <- 0 à taille - 1 
    posAlea1 <- aléa(0, taille - 1) 
    posAlea2 <- aléa(0, taille - 1)  
    Si posAlea1 ≠ posAlea2 Alors 
     ...