Généricité
En programmation, la généricité (ou programmation générique), consiste à définir des algorithmes identiques opérant sur des données de types différents. On définit de cette façon des procédures ou des types entiers génériques. On pourrait ainsi programmer une pile, ou une procédure qui prend l'élément supérieur de la pile, indépendamment du type de données contenues.
C'est donc une forme de polymorphisme, le « polymorphisme de type » dit aussi « paramétrage de type » : en effet, le type de donnée général (abstrait) apparaît comme un paramètre des algorithmes définis, avec la particularité que ce paramètre-là est un type. C'est un concept important pour un langage de haut niveau car il permet d'écrire des algorithmes généraux opérant sur toute une série de types : la généricité augmente donc le niveau d'abstraction des programmes écrits dans un langage qui possède cette fonctionnalité. Divers mécanismes ont été conçus pour permettre la programmation générique.
Polymorphisme
Le polymorphisme est le concept consistant à fournir une interface unique à des entités pouvant avoir différents types. Ce mécanisme est supporté par tous les langages orientés objets, y compris ceux que l'on qualifie généralement comme étant à typage statique (Java ou méthodes virtuelles de C++ par exemple). En effet, même si le langage apporte des outils pour vérifier la cohérence des interfaces à la compilation, la détermination du type réel de l'argument n'est effectuée qu'à l'exécution dans certains cas.
Principe
Un avantage de la programmation générique consiste à abstraire un ensemble de concepts cohérents pour construire des algorithmes au-dessus indépendamment de leur implémentation. Ainsi la fonction de tri de la STL a besoin en entrée d'itérateurs aléatoires sur une collection d'objets et d'une fonction de comparaison sur ces objets ; cela rend ainsi cette fonction utilisable dans de très nombreux contextes.
Ce mécanisme permet une programmation générique purement statique puisque l'ensemble des résolutions de types est fait au moment de la compilation. Par exemple, si l'on veut créer une liste générique, une déclaration (ici en utilisant les templates de C++) serait :
List<T>
, où T
représente un type. À l'instanciation, on pourrait créer une List<int>
ou une List<Animal>
. La liste est ensuite traitée comme si le type correspondant avait été spécifié.
Langages d'implémentation
La programmation générique est supportée par certains langages à typage statique dans lesquels il est possible d'utiliser des types génériques ou « templates ». La toute première implémentation fut en Ada 83, bien avant les templates de C++ qui ont popularisé la programmation générique, en permettant d'écrire du code sans considérer explicitement les types de données avec lesquels le code sera utilisé. Ceci est intensivement utilisé dans la Standard Template Library (STL).
D'autres langages de programmation, comme Java depuis la version 1.5 ou encore Eiffel, supportent la programmation générique. OCaml permet également une programmation générique grâce au système de modules paramétrés qu'il propose. VB.NET et C# la supportent depuis la version 2.0 du framework .NET.
Surcharge et multi-méthodes
Dans certains langages, il est possible d'utiliser la surcharge des opérateurs ou des fonctions. Cette technique consiste à donner le même nom à plusieurs fonctions sémantiquement équivalentes mais agissant sur des paramètres de type différent. Lorsqu'il rencontre ce nom de fonction, le compilateur choisit l'implémentation la plus adaptée en se basant sur le nombre et le type des arguments. Des langages comme CLOS étendent ce principe en choisissant parmi les fonctions homonymes non plus à la compilation mais à l'exécution. Ce mécanisme est appelé multi-méthode et est souvent présenté par ailleurs comme une extension du polymorphisme.
Autres techniques
Il est également possible de réaliser des fonctions génériques dans des langages non objets. C'est ainsi que les deux outils utilisés pour développer en C des fonctions génériques sont les pointeurs génériques (void *
) pour rendre génériques les arguments et les pointeurs de fonction. Par exemple la fonction qsort de la bibliothèque standard accepte en argument un pointeur sur une fonction chargée de comparer les éléments à trier.