Polimorfizmus (informatika)A programozási nyelvekben és a típuselméletben a polimorfizmus (a görög πολύς, sok és a μορφή, alak szavakból) egy egységes interfészre utal, amit különböző típusok valósítanak meg.[1] A polimorf típuson végzett műveletek több különböző típus értékeire alkalmazhatók.[2] Polimorfizmus többféleképpen is megvalósítható:
A paraméteres és az altípusos polimorfizmus kapcsolata elvezet a variancia és a korlátos minősítés, más néven korlátozott polimorfizmus fogalmához. TörténeteElőször az angol Christopher Strachey írta le 1967-ben az ad hoc és a paraméteres polimorfizmust előadásjegyzeteiben.[4] Peter Wegner és Luca Cardelli egy 1985-ben megjelent cikkükben bevezette a beágyazási polimorfizmust az altípusosság és az öröklődés modellezésére. Maga az altípusosság implementációja megelőzte ezt a cikket,[2] a Simula 1967 programnyelv már tartalmazott öröklődést és altípusosságot. TípusaiAz implicit típuskonverziót szintén a polimorfizmus egy fajtájának tekintik, ez a kényszerítési polimorfizmus.[2][5] Ad hoc polimorfizmusChris Strachey az ad hoc polimorfizmus elnevezést arra az esetre használta, amikor az argumentumok különböző típusúak lehetnek, és a függvény ennek megfelelően különféleképpen viselkedhet, amit túlterheléssel lehet elérni. Ha több különböző típusra is ugyanúgy viselkedik a függvény, akkor érdemes a másik két polimorfizmus egyikére áttérni. Itt az ad hoc kifejezés nem pejoratív, hanem arra utal, hogy nem következik a típusrendszerből. Az alábbi Pascal/Delphi példában az Add függvény a hívások szerint különféle típusokra működik, de a fordító minden tekintetben külön függvényként kezeli őket. 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, ', 'World!')); (* Prints "Hello, World!" *)
end.
Dinamikusan típusozott nyelvekben a helyzet még bonyolultabb, mert csak futásidőben lehet meghatározni, hogy melyik függvényt kell meghívni. Paraméteres polimorfizmusA parametrikus polimorfizmus jellemzője, hogy a függvényt egyszer kell megírni, és az minden típusra egységesen fog működni. A függvény paramétereinek típusát a függvény használatakor paraméterben kell megadni, innen a paraméteres név. Szokták generikusnak is hívni, az egységes viselkedés miatt. Használata a teljes típusbiztonság megőrzésével növeli a nyelv kifejezőerejét. A paraméteres polimorfizmus nemcsak függvényekre, hanem adattípusokra is értelmezhető, tehát adattípusokra is lehetnek paraméteresek, más néven generikusok. A generikus típusok polimorfikus adattípusok néven is megtalálhatók. A parametrikus polimorfizmus nagyon gyakori a funkcionális programozásban, emiatt gyakran csak polimorfizmusnak nevezik. Az alábbi Haskell példa egy paraméteres polimorf lista adattípust mutat be, két paraméteresen polimorf függvénnyel. 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)
Objektumorientált nyelvekben kevésbé gyakori, de legtöbbjükben szintén van lehetőség paraméteres polimorfizmusra. Ezt C++-ban és D-ben template-ek, Javában genericek biztosítják. Az alábbi példa C#-ban íródott: 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) {
...
}
John C. Reynolds és később Jean-Yves Girard formálisan ezt a jelölést vezette be a lambda-kalkulusba, és a polimorfikus lambda-kalkulust F-rendszernek nevezte. A paraméteres polimorfikus függvények inkább az adatok formájával, mint értékükkel dolgozhatnak, általánosan, típusfüggetlen módon. Igazi képességeik metaprogramozással bontakoztathatók ki. AltípusosságEgyes nyelvekben az altípusosság használható a polimorfizmus korlátozására. Ezekben a nyelvekben az altípusosság lehetővé teszi, hogy egy függvény egy bizonyos T típusú objektumot használjon, de ha S altípusa T-nek, akkor a függvény S típusú objektumokra is használható a Liskov-féle helyettesíti elv szerint. Az altípus jelölése néha S <: T. Megfordítva, T szupertípusa S-nek, ennek jele T :> S. Az altípusos polimorfizmus rendszerint dinamikus. A következő példában a kutya és a macska az állatok altípusa. A letsHear() eljárás állatot fogad, de altípusos objektumokkal is működik. abstract class Animal {
abstract String talk();
}
class Cat extends Animal {
String talk() {
return "Meow!";
}
}
class Dog extends Animal {
String talk() {
return "Woof!";
}
}
void letsHear(final Animal a) {
println(a.talk());
}
int main() {
letsHear(new Cat());
letsHear(new Dog());
}
A következő példában Number, Rational, és Integer típusok úgy, hogy Number :> Rational és Number :> Integer. Egy függvény, aminek paramétere Number, úgy működik Integer vagy Rational paraméterrel, mint Number paraméterrel. Az aktuális típus elrejthető a felhasználó elől, és objektumazonossággal férhető hozzá. Valójában, ha a Number típus absztrakt, akkor nem is lehetnek olyan objektumok, amelyek aktuális típusa Number. Lásd: absztrakt osztály, absztrakt adattípus. Ez a típushierarchia számtoronyként is ismert a Scheme programozási nyelvből, és rendszerint még több típusból áll. Az objektumorientált nyelvek az altípusos polimorfizmust öröklődéssel valósítják meg. Tipikus implementációkban az osztályok tartalmaznak virtuális táblát, ami hivatkozza az osztály interfészének polimorf részét megvalósító függvényeit. Minden objektum tartalmaz egy referenciát erre a táblára, amire mindig szükség van, ha valami egy polimorf függvényt hív. Ez a mechanizmus példa a:
Néhány objektumrendszer, például a Common Lisp Object System többszörös küldésre is képes, így a hívások az összes argumentumokban polimorfak lesznek. PolitípusosságFunkcionális környezetben hasonló fogalom a politípusosság, ami még általánosabb, mint a polimorf. Egy ilyen függvényben, habár bizonyos speciális típusokra megadhatók ad hoc esetek, nem tartalmaz ad hoc kombinátort.[6] Statikus és dinamikus polimorfizmusA polimorfizmus lehet statikus vagy dinamikus, ezt az implementáció kiválasztása dönti el. Ez statikus, illetve dinamikus kötés néven ismert. A statikus végrehajtása gyorsabb, mivel nem kell dinamikus kötéseket figyelni, viszont a fordítónak többet kell dolgoznia, a program lassabban és több memóriát igénybe véve fordul. Továbbá megkönnyíti a kód elemzését statikus elemző eszközök többet látnak belőle, és az ember számára is érthetőbb. A dinamikus rugalmasabb, de lassítja a futást, tehetővé teszi a kacsaszerű típusosságot (duck typing). Egy dinamikusan linkelt könyvtár az objektumokat azok pontos típusának ismerete nélkül is képes kezelni. Általában statikus az ad hoc és a paraméteres polimorfizmus, és dinamikus az altípusos. Ezzel szemben elérhető a statikus altípusosság metaprogramozással, pontosabban CRTP-vel (curiously recurring template pattern). Jegyzetek
|