Polimorfizmi (shkencë kompjuterike)

Në teorinë e gjuhëve të programimit dhe teorinë e tipeve, **polimorfizmi** është përdorimi i një simboli të vetëm për të përfaqësuar shumë lloje të ndryshme. 

Në programimin e orientuar nga objekti, polimorfizmi është sigurimi i një ndërfaqeje të vetme për entitete të llojeve të ndryshme.  Koncepti është huazuar nga një parim në biologji ku një organizëm ose specie mund të ketë shumë forma ose faza të ndryshme. 

Format kryesore më të njohura të polimorfizmit janë:

* *Polimorfizmi ad hoc* : përcakton një ndërfaqe të përbashkët për një bashkësi arbitrare të tipeve të specifikuara secili më vete.
* *Polimorfizmi parametrik* : nuk specifikon tipet konkrete dhe në vend të kësaj përdor simbole abstrakte që mund të zëvendësojnë çdo tip.
* *Nëntipi* (i quajtur edhe *polimorfizëm i nëntipit* ose *polimorfizëm i përfshirjes* ): kur një emër tregon shembuj të shumë klasave të ndryshme të lidhura nga një superklasë e përbashkët. 

## Format 
### Polimorfizmi ad hoc 
Christopher Strachey zgjodhi termin *polimorfizëm ad hoc* për t'iu referuar funksioneve polimorfike që mund të aplikohen në argumente të llojeve të ndryshme, por që sillen ndryshe në varësi të llojit të argumentit në të cilin aplikohen (i njohur edhe si mbingarkimi e funksionit ose mbingarkimi i operatorit ).  Termi " ad hoc " në këtë kontekst nuk synohet të jetë pezhorativ; i referohet thjesht faktit që kjo formë polimorfizmi nuk është një tipar themelor i sistemit të tipit. Në shembullin Java më poshtë, funksionet `Add` duket se funksionojnë përgjithësisht mbi dy lloje (numër të plotë dhe varg) kur shikohen thirrjet, por konsiderohen të jenë dy funksione krejtësisht të dallueshme nga përpiluesi për të gjitha qëllimet dhe qëllimet:
```java
class AdHocPolymorphic {
  public String add(int x, int y) {
    return "Sum: "+(x+y);
  }

  public String add(String name) {
    return "Added "+name;
  }
}

public class adhoc {
  public static void main(String[] args) {
    AdHocPolymorphic poly = new AdHocPolymorphic();

    System.out.println( poly.add(1,2)  ); // prints "Sum: 3"
    System.out.println( poly.add("Jay") ); // prints "Added Jay"
  }
}
```
Në gjuhët e shtypura në mënyrë dinamike situata mund të jetë më komplekse pasi funksioni i saktë që duhet të thirret mund të përcaktohet vetëm në kohën e ekzekutimit.

### Polimorfizmi parametrik 
*Polimorfizmi parametrik* lejon që një funksion ose një lloj të dhënash të shkruhet në mënyrë gjenerike, në mënyrë që të mund të trajtojë vlerat *në mënyrë uniforme*, pa u varur nga lloji i tyre.  Polimorfizmi parametrik është një mënyrë për ta bërë një gjuhë më shprehëse duke ruajtur ende sigurinë e plotë të tipit statik.

Koncepti i polimorfizmit parametrik zbatohet si për tipet e të dhënave ashtu edhe për funksionet. Një funksion që mund të vlerësohet ose të zbatohet mbi vlera të llojeve të ndryshme njihet si *funksion polimorfik.* Një lloj i të dhënave që mund të duket se është i një lloji të përgjithësuar (p.sh. një listë me elementë të tipit arbitrar) caktohet *lloji polimorfik i të dhënave* si lloji i përgjithësuar nga i cili janë bërë specializime të tilla.

Polimorfizmi parametrik është i kudondodhur në programimin funksional, ku shpesh quhet thjesht "polimorfizëm". Shembulli i mëposhtëm në Haskell tregon një lloj të dhënash të listës së parametrizuar dhe dy funksione parametrikisht polimorfikë mbi to:

```haskell
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)
```
Polimorfizmi parametrik është gjithashtu i disponueshëm në disa gjuhë të orientuara nga objekti. Për shembull, template në C++ dhe D, ose nën emrin generics në C#, Delphi, Java dhe Go:
```csharp
class List {
  class Node {
    T elem;
    Node next;
  }
  Node head;
  int length() { ... }
}

List map(Func f, List xs) {
  ...
}
```

### Nëntipi 
Disa gjuhë përdorin idenë e *nëntipit* (i quajtur edhe *polimorfizëm i nëntipit* ose *polimorfizëm i përfshirjes* ) për të kufizuar gamën e llojeve që mund të përdoren në një rast të veçantë të polimorfizmit. Në këto gjuhë, nëntipi lejon që një funksion të shkruhet për të marrë një objekt të një tipi të caktuar *T*, por edhe të funksionojë saktë, nëse kalon një objekt që i përket një lloji *S* që është nënlloj i *T* (sipas parimit të zëvendësimit të Liskovit ) . Kjo lidhje e tipit ndonjëherë shkruhet *S*&nbsp;&#x3C; :&nbsp;*T* . Anasjelltas, *T* thuhet se është një *supertip* i *S* -të shkruar *T*&nbsp;: &#x3E;&nbsp;*S* . Polimorfizmi i nëntipit zakonisht zgjidhet në mënyrë dinamike (shih më poshtë).

Në shembullin e mëposhtëm Java, ne i bëjmë macet dhe qentë nëntipe të kafshëve shtëpiake. Procedura `letsHear()` pranon një kafshë shtëpiake, por gjithashtu do të funksionojë si duhet nëse i kalohet një nëntip:
```java
abstract class Pet {
  abstract String speak();
}

class Cat extends Pet {
  String speak() {
    return "Meow!";
  }
}

class Dog extends Pet {
  String speak() {
    return "Woof!";
  }
}

static void letsHear(final Pet pet) {
  println(pet.speak());
}

static void main(String[] args) {
  letsHear(new Cat());
  letsHear(new Dog());
}
```
Skeda:UML_class_pet.svg