En programmation orientée objet , le polymorphisme consiste à fournir une interface unique à des entités de types de données différents . Ce concept est emprunté à un principe de la biologie selon lequel un organisme ou une espèce peut présenter de nombreuses formes ou stades différents.
Les principales formes de polymorphisme les plus couramment reconnues sont :
- Polymorphisme ad hoc : définit une interface commune pour un ensemble arbitraire de types spécifiés individuellement.
- Polymorphisme paramétrique : ne spécifie pas de types concrets et utilise plutôt des symboles abstraits qui peuvent se substituer à n’importe quel type.
- Sous-typage (également appelé polymorphisme de sous-type ou polymorphisme d'inclusion ) : lorsqu'un nom désigne des instances de nombreuses classes différentes liées par une superclasse commune.
Dans un article de 1985, Peter Wegner et Luca Cardelli ont introduit le terme polymorphisme d'inclusion pour modéliser les sous-types et l'héritage , citant Simula comme le premier langage de programmation à l'implémenter.
Formulaires
polymorphisme ad hoc
addfonctions semblent fonctionner de manière générique sur deux types ( entier et chaîne de caractères ) lors de l'examen des appels, mais sont considérées comme des fonctions entièrement distinctes par le compilateur :Dans les langages à typage dynamique, la situation peut être plus complexe, car la fonction correcte à appeler ne peut être déterminée qu'à l'exécution.
La conversion implicite de type a également été définie comme une forme de polymorphisme, appelée « polymorphisme de coercition ».
polymorphisme paramétrique
Le concept de polymorphisme paramétrique s'applique aussi bien aux types de données qu'aux fonctions . Une fonction qui peut s'évaluer ou s'appliquer à des valeurs de types différents est dite polymorphe. Un type de données pouvant se comporter comme un type généralisé (par exemple, une liste dont les éléments sont de types arbitraires) est qualifié de type de données polymorphe comme le type généralisé à partir duquel ces spécialisations sont effectuées.
Le polymorphisme paramétrique est omniprésent en programmation fonctionnelle, où il est souvent simplement appelé « polymorphisme ». L’exemple suivant en Haskell présente un type de données liste paramétré et deux fonctions polymorphes paramétriques sur ce type :
Liste de données a = Nil | Cons a ( Liste a )longueur :: Liste a -> Entier longueur Nil = 0 longueur ( Cons x xs ) = 1 + longueur xsmap :: ( a -> b ) -> List a -> List b map f Nil = Nil map f ( Cons x xs ) = Cons ( f x ) ( map f xs )
Le polymorphisme paramétrique est également disponible dans plusieurs langages orientés objet. Par exemple, les modèles en C++ et D , ou sous le nom de génériques en C# , Delphi , Java et Go :
Sous-typage
![]()
Dans un autre exemple, si `Number` , `Rational` et `Integer` sont des types tels que ''Rational''"}},"i":0}}] `Number :> Rational` et ''Integer''"}},"i":0}}] `Number :> Integer` ( Rational et Integer étant des sous-types d'un type `Number` qui est un supertype de ces types), une fonction écrite pour prendre un `Number` fonctionnera aussi bien avec un ` Integer` ou un `Rational` qu'avec un `Number` . Le type réel de l'objet peut être masqué aux clients et accessible via son identité . Si le type `Number` est abstrait , il peut être impossible d'accéder à un objet dont le type le plus dérivé est `Number` (voir type de données abstrait , classe abstraite ). Ce type particulier de hiérarchie de types est connu, notamment dans le contexte du langage Scheme , sous le nom de « tour numérique » et contient généralement beaucoup plus de types.
Les langages de programmation orientés objet offrent le polymorphisme de sous-type grâce à l' héritage (ou sous-classement ). Dans les implémentations classiques, chaque classe contient une table virtuelle (ou vtable ) – une table des fonctions implémentant la partie polymorphe de l'interface de la classe – et chaque objet contient un pointeur vers la vtable de sa classe, qui est consultée lors de l'appel d'une méthode polymorphe. Ce mécanisme est un exemple de :
- liaison tardive , car les appels de fonctions virtuelles ne sont liés qu'au moment de l'invocation ;
- dispatch unique (c'est-à-dire polymorphisme à un seul argument), car les appels de fonctions virtuelles sont liés simplement en parcourant la vtable fournie par le premier argument (l'
thisobjet), de sorte que les types d'exécution des autres arguments sont totalement hors de propos.
Il en va de même pour la plupart des autres systèmes d'objets populaires. Certains, cependant, comme Common Lisp Object System , offrent la multidispatch , grâce à laquelle les appels de méthodes sont polymorphes pour tous leurs arguments.
L’interaction entre le polymorphisme paramétrique et le sous-typage conduit aux concepts de variance de type et de quantification bornée .
polymorphisme de lignes
Polytypisme
Aspects de mise en œuvre
Polymorphisme statique et dynamique
Le polymorphisme statique s'exécute plus rapidement, car il n'y a pas de surcharge liée à la répartition dynamique, mais il nécessite une prise en charge supplémentaire du compilateur. De plus, il permet une analyse statique plus poussée par les compilateurs (notamment pour l'optimisation), les outils d'analyse de code source et les programmeurs. Le polymorphisme dynamique est plus flexible, mais plus lent ; par exemple, il permet le typage dynamique (ou « duck typing » ) et une bibliothèque liée dynamiquement peut manipuler des objets sans connaître leur type complet.
Le polymorphisme statique se rencontre généralement dans le polymorphisme ad hoc et le polymorphisme paramétrique, tandis que le polymorphisme dynamique est courant pour le polymorphisme de sous-type. Cependant, il est possible d'obtenir un polymorphisme statique avec sous-typage grâce à une utilisation plus sophistiquée de la métaprogrammation par modèles , notamment le modèle curieusement récurrent .
Lorsque le polymorphisme est exposé via une bibliothèque , le polymorphisme statique devient impossible pour les bibliothèques dynamiques , car il est impossible de connaître les types des paramètres lors de la construction de l' objet partagé . Alors que des langages comme C++ et Rust utilisent des modèles monomorphes , le langage de programmation Swift recourt largement à la répartition dynamique pour construire par défaut l' interface binaire de l'application pour ces bibliothèques. De ce fait, davantage de code peut être partagé pour une taille système réduite, au prix d'une surcharge d'exécution.