miniatura|Clasele majore de polimorfism În și , polimorfismul este furnizarea unei singure interfețe de către entități de diferite tipuri, sau utilizarea unui singur simbol pentru a reprezenta mai multe tipuri diferite.: "Polymorphic types are types whose operations are applicable to values of more than one type." Conceptul este împrumutat de la un principiu din biologie în care un organism sau o specie poate avea multe forme sau faze diferite.
Cele mai frecvent recunoscute clase majore de polimorfism sunt:
: se definește o interfață comună pentru un set arbitrar de tipuri specificate individual. : când unul sau mai multe tipuri nu sunt specificate prin nume, ci prin simboluri abstracte care pot reprezenta orice tip. (numită și polimorfism de subtip sau polimorfism de incluziune): atunci când un nume denotă instanțe ale mai multor clase diferite legate de o superclasă comună.
Istorie Interesul pentru polimorfe s-a dezvoltat semnificativ în anii 1960, implementările practice începând să apară până la sfârșitul acelui deceniu. Polimorfismul ad-hoc și polimorfismul parametric au fost descrise inițial în ale lui Christopher Strachey, unde sunt enumerate ca „cele două clase principale” de polimorfism. Polimorfismul ad-hoc era o caracteristică a limbajului , în timp ce polimorfismul parametric a fost caracteristica de bază a sistemului de tipare din .
Într-o lucrare din 1985, și au introdus termenul de polimorfism de incluziune pentru a modela subtipurile și , dând Simula drept primul limbaj de programare care îl implementează.
Tipurile
Polimorfismul ad-hoc Christopher Strachey a ales termenul de polimorfism ad-hoc cu referire la funcțiile polimorfe care pot fi aplicate argumentelor de diferite tipuri, dar care se comportă diferit în funcție de tipul argumentului căruia îi sunt aplicate (cunoscut și ca supraîncărcare a funcțiilor sau supraîncărcare a operatorilor). Termenul „ad hoc” în acest context nu se dorește a fi peiorativ; se referă pur și simplu la faptul că acest tip de polimorfism nu este o trăsătură fundamentală a sistemului de tipare. În exemplul Pascal/Delphi de mai jos, funcțiile Add par să funcționeze generic pe diferite tipuri atunci când privim invocările, dar sunt considerate a fi două funcții complet distincte de către compilator:program Adhoc;
function Add(x, y : Integer) : Integer; begin Add := x + y end;
function Add(s, t : String) : String; begin Add := Concat(s, t) end;
begin Writeln(Add(1, 2)); (* Prints "3" *) Writeln(Add('Hello, ', 'Mammals!')); (* Prints "Hello, Mammals!" *) end.În limbile , situația poate fi mai complexă, deoarece funcția care trebuie invocată se poate determina doar la momentul execuției.
a fost definită și ea ca o formă de polimorfism, denumită „polimorfism de constrângere”.
Polimorfismul parametric Polimorfismul parametric permite ca o funcție sau un tip de date să fie scris generic, astfel încât să poată gestiona valorile în mod uniform, fără a depinde de tipul lor. Polimorfismul parametric este o modalitate de a face un limbaj mai expresiv, menținând în același timp un complet static.
Conceptul de polimorfism parametric se aplică atât tipurilor de date, cât și . O funcție care poate evalua sau care se aplica unor valori de diferite tipuri este denumită funcție polimorfă. Un tip de date care poate părea un tip generalizat (de exemplu, o cu elemente de tip arbitrar) se numește tip de date polimorf ca și tipul generalizat din care sunt făcute astfel de specializări.
Polimorfismul parametric este omniprezent în programarea funcțională, unde este adesea numit pur și simplu „polimorfism”. Următorul exemplu din Haskell prezintă o listă parametrizată și două funcții polimorfe parametric definite pe acestea:data List a = Nil | Cons a (List a)
length :: List a -> Integer length Nil = 0 length (Cons x xs) = 1 + length xs
map :: (a -> b) -> List a -> List b map f Nil = Nil map f (Cons x xs) = Cons (f x) (map f xs)Polimorfismul parametric este disponibil și în mai multe limbaje orientate pe obiecte. De exemplu, template-urile din C++ și D, sau sub din C#, Delphi, Java și Go:class List<T> { class Node<T> { T elem; Node<T> next;
Node<T> head; int length() { ... } }
List<B> map(Func<A, B> f, List<A> xs) { ... } (și mai târziu ) au dezvoltat formal această noțiune de polimorfism ca o extensie a calculului lambda (numit calcul lambda polimorf sau ). Orice funcție polimorfă parametric are automat unele restricții asupra a ceea ce poate face ea: poate lucra doar asupra formei datelor, și nu pe valoarea acestora, ceea ce conduce la conceptul de parametricitate.
Subtiparea Unele limbaje folosesc ideea de subtipare (numită și polimorfism de subtip sau polimorfism de incluziune) pentru a restricționa gama de tipuri care pot fi utilizate într-un anumit caz de polimorfism. În aceste limbaje, subtiparea permite scrierea unei funcții în așa fel încât primește ca parametru un obiect de un anumit tip T, dar ar funcționa corect și dacă primește un obiect care aparține unui tip S care este un subtip al lui T (conform ). Această relație între tipuri se scrie uneori S < : T. Invers, se spune că T este un supertip al lui S scris T : > S. Polimorfismul de subtip este de obicei rezolvat dinamic (vezi mai jos).
În următorul exemplu Java, pisicile și câinii sunt subtipuri ale tipului animal. Procedura letsHear() acceptă un animal, dar va funcționa corect și dacă îi este transmis un subtip:abstract class Animal { abstract String talk(); }
class Cat extends Animal { String talk() { return "Meow!";
class Dog extends Animal { String talk() { return "Woof!";
static void letsHear(final Animal a) { println(a.talk()); }
static void main(String[] args) { letsHear(new Cat()); letsHear(new Dog()); }Într-un alt exemplu, dacă Number, Rational și Integer sunt tipuri astfel încât Number : > Rational și Number : > Integer, o funcție care primește un Number va functiona la fel de bine atunci cand trece un Integer sau un Rational ca și atunci cand primește un Number. Tipul real al obiectului poate fi ascuns clienților într-o și accesat prin obiectului. De fapt, dacă tipul Number este abstract, s-ar putea să nu fie nici măcar posibilă primirea unui obiect al cărui tip cel mai derivat este Number. Acest tip special de ierarhie de tipuri este cunoscut – mai ales în contextul limbajului de programare Scheme – drept și, de obicei, conține mai multe tipuri.
Limbajele de programare orientate pe obiecte oferă polimorfism de subtipuri folosind (cunoscută și sub numele de ). În implementările tipice, fiecare clasă conține ceea ce se numește o tabelă de funcții care implementează partea polimorfă a interfeței clasei și fiecare obiect conține un pointer către tabela virtuală a clasei sale, care este apoi consultat ori de câte ori se apelează o metodă polimorfă. Acest mecanism este un exemplu de:
, deoarece apelurile de funcții virtuale nu sunt legate până la momentul invocării; (adică polimorfism cu un singur argument), deoarece apelurile de funcții virtuale sunt legate pur și simplu prin căutarea prin tabela virtuală furnizată de primul argument (obiectul this ), astfel încât tipurile celorlalte argumente de la momentul rulări sunt complet irelevante.
Același lucru este valabil și pentru majoritatea celorlalte sisteme de obiecte. Unele, totuși, cum ar fi , oferă , în care apelurile de metodă sunt polimorfe în toate argumentele.
Interacțiunea dintre polimorfismul parametric și subtipare duce la conceptele de și .
Polimorfismul de rând Polimorfismul de rând este un concept similar, dar distinct de subtipare. Se ocupă de . Permite utilizarea tuturor valorilor ale căror tipuri au anumite proprietăți, fără a pierde informațiile de tip rămase.
Aspecte de implementare
Polimorfism static și dinamic Polimorfismul poate fi clasificat și după momentul în care este selectată implementarea: static (la compilare) sau dinamic (la rulare, de obicei printr-o funcție virtuală). Aceasta este cunoscută, respectiv, sub denumirea de și , iar formele corespunzătoare de polimorfism sunt denumite, în consecință, polimorfism static și polimorfism dinamic.
Polimorfismul static se execută mai rapid, deoarece nu există un overhead de dynamic dispatch, dar necesită suport suplimentar în compilator. În plus, polimorfismul static permite o analiză statică mai mare a compilatarelor (în special pentru optimizare), instrumente de analiză a codului sursă și cititori umani (programatori). Polimorfismul dinamic este mai flexibil, dar mai lent de exemplu, polimorfismul dinamic permite duck typing, iar o bibliotecă legată dinamic poate funcționa pe obiecte fără a cunoaște tipul lor complet.
Polimorfismul static apare de obicei în polimorfismul ad-hoc și în polimorfismul parametric, în timp ce polimorfismul dinamic este obișnuit pentru polimorfismul de subtip. Cu toate acestea, se poate obține polimorfism static cu subtipare prin utilizarea mai sofisticată a , și anume .
Când polimorfismul este expus printr-o , polimorfismul static devine imposibil pentru deoarece nu există nicio modalitate de a ști ce tipuri au parametrii la momentul când este construit shared objectul. În timp ce limbaje precum C++ și Rust folosesc template-uri , limbajul de programare Swift utilizează în mod implicit dynamic dispatch pentru a construi pentru aceste biblioteci. Ca rezultat, se poate partaja mai mult cod pentru o dimensiune redusă a sistemului cu costul unui overhead la rulare.
Note
Categorie:Tipuri de date