A többszörös metódusfeloldás vagy multimetódusok egyes programozási nyelvek olyan jellemzője, amelyben egy függvény vagy metódus dinamikusan metódusfeloldásra kerülhet a futásidő (dinamikus) típusa vagy általánosabb esetben több argumentumának valamilyen más attribútuma alapján. Ez az egyszeri metódusfeloldású polimorfizmus általánosítása, ahol egy függvény vagy metódus hívása dinamikusan metódusfeloldásra kerül annak az objektumnak a származtatott típusa alapján, amelyen a metódust hívták. A többszörös metódusfeloldás a dinamikus metódusfeloldást a végrehajtó függvényhez vagy metódushoz irányítja egy vagy több argumentum kombinált jellemzőinek felhasználásával.

A metódusfeloldás megértése A számítógépes szoftverek fejlesztői a forráskódot általában megnevezett blokkokba szervezik, amelyeket különbözőképpen neveznek szubrutinoknak, eljárásoknak, alprogramoknak, függvényeknek vagy módszereknek. A függvényben lévő kódot a függvény meghívásával - a nevére hivatkozó kódrészlet lefuttatásával - hajtják végre. Ez átmenetileg átadja a vezérlést a hívott függvénynek; amikor a függvény végrehajtása befejeződött, a vezérlés jellemzően visszakerül a hívó utasításhoz, amely a hivatkozást követi.

A függvényneveket általában úgy választják ki, hogy azok leírják a függvény feladatát. Néha kívánatos, hogy több függvénynek ugyanazt a nevet adjuk, gyakran azért, mert gyakorlatilag hasonló feladatokat látnak el, de különböző típusú bemeneti adatokkal dolgoznak. Ilyen esetekben a függvényhívás helyén lévő névreferencia nem elegendő a végrehajtandó kódblokk azonosításához. Ehelyett a függvényhívás argumentumainak száma és típusa is felhasználásra kerül a több függvény megvalósítása közötti kiválasztáshoz.

A hagyományosabb, azaz az egyszeres metódusfeloldású objektumorientált programozási nyelvekben egy metódus meghívásakor (Smalltalkban egy üzenet küldése, C++-ban egy tagfüggvény hívása) az egyik argumentumot speciálisan kezelik, és arra használják, hogy meghatározzák, hogy az adott nevű metódusok (potenciálisan sok) osztálya közül melyiket kell alkalmazni. Sok nyelvben a speciális argumentumot szintaktikailag jelzik; például számos programozási nyelv a speciális argumentumot egy pont elé teszi a metódushívás során: special.method(other, arguments, here), így a lion.sound() üvöltést, míg a sparrow.sound() csiripelést eredményezne.

Ezzel szemben a többszörös metódusfeloldást használó nyelvekben a metódus kiválasztása egyszerűen az alapján történik, hogy melyik metódus az, amelynek argumentumai megfelelnek a meghívásakor használt argumentumok számával és típusával. Nem használnak olyan speciális argumentumot a függvényhívás során, amely egy adott függvény/metódus „tulajdonosa” lenne.

A Common Lisp Object System (CLOS) a többszörös metódusfeloldás korai és jól ismert példája.

Adattípusok Amikor olyan nyelvekkel dolgozunk, amelyek képesek az adattípusok megkülönböztetésére fordítási időben, akkor az alternatívák közötti választás már ekkor megtörténhet. Az ilyen alternatív függvények létrehozását a fordítási idejű kiválasztáshoz általában egy függvény túlterhelésnek nevezik.

Azokban a programozási nyelvekben, amelyek az adattípusok azonosítását a futási időre halasztják (azaz késői kötés), az alternatív függvények közötti választásnak ekkor kell megtörténnie, a függvény argumentumainak dinamikusan meghatározott típusai alapján. Azokat a függvényeket, amelyek alternatív implementációi ilyen módon kerülnek kiválasztásra, legáltalánosabban multimetódusoknak nevezzük.

A függvényhívások dinamikus metódusfeloldásnak van némi futásidő költsége. Egyes nyelvekben a túlterhelés és a multimetódusok függvényhívás közötti különbségtétel elmosódhat, és a fordító határozza meg, hogy a fordítási idejű kiválasztás alkalmazható-e egy adott függvényhívásra, vagy pedig lassabb futási idejű metódusfeloldásra van szükség.

Gyakorlati alkalmazás Annak becslésére, hogy a gyakorlatban milyen gyakran használják a többszörös metódusfeloldást, Muschevici et al. olyan programokat vizsgált, amelyek dinamikus metódusfeloldást használnak. Kilenc, többnyire fordítóprogramot elemeztek, amelyeket hat különböző nyelven írtak: Ezek a nyelvek a Common Lisp Object System, a Dylan, a Cecil, a MultiJava, a Diesel és a Nice. Eredményeik azt mutatják, hogy a generikus függvények 13-32%-a használja egy argumentum dinamikus típusát, míg 2,7-6,5%-uk több argumentum dinamikus típusát. A generikus függvények fennmaradó 65-93%-ának egy konkrét metódusa van (overrider), és így úgy tekintik, hogy nem használják argumentumaik dinamikus típusát. A tanulmány továbbá arról számol be, hogy a generikus függvények 2-20%-ának két, 3-6%-ának pedig három konkrét függvény implementációja van. A számok gyorsan csökkennek a több konkrét overriderrel rendelkező függvények esetében.

A többszörös metódusfeloldást sokkal erőteljesebben használják a Juliában, ahol a többszörös metódusfeloldást a nyelv eredetétől fogva központi tervezési koncepciónak tekintették: a Muschevici által elemzett, az általános függvényenkénti átlagos metódusszámra vonatkozó statisztikákat összegyűjtve kiderült, hogy a Julia szabványkönyvtárában több mint kétszer annyi túlterhelést használnak, mint a Muschevici által elemzett többi nyelvben, és több mint tízszer annyit a bináris operátorok esetében.

Az ezekből a tanulmányokból származó adatokat a következő táblázatban foglaljuk össze, ahol a DR metódusfeloldási arány (dispatch ratio) az egy általános függvényre jutó módszerek átlagos száma; a CR választási arány (choice ratio) a módszerek számának négyzetének átlaga (a nagyszámú módszerrel rendelkező függvények gyakoriságának jobb mérése érdekében); a DoS specializációs fok pedig a típus-specifikált argumentumok átlagos száma metódusonként (azaz a feladott argumentumok száma): NyelvÁtlag # metódusok (DR)Választási arány (CR)Feladott argumentumok száma (DoS)Cecil2.3363.301.06Common Lisp (CMU)2.036.341.17Common Lisp (McCLIM)2.3215.431.17Common Lisp (Steel Bank)2.3726.571.11Diesel2.0731.650.71Dylan (Gwydion)1.7418.272.14Dylan (OpenDylan)2.5143.841.23Julia5.8651.441.54Julia (operators only)28.1378.062.01MultiJava1.508.921.02Nice1.363.460.33

Elmélet A többszörös metódusfeloldású nyelvek elméletét először Castagna és társa dolgozta ki, a késői kötéssel rendelkező, túlterhelt függvények modelljének meghatározásával. Ez eredményezte az objektumorientált nyelvek kovariancia és kontravariancia problémájának első formalizálását és a bináris módszerek problémájának megoldását.

Példák A többszörös és az egyszeri metódusfeloldás megkülönböztetését egy példával lehet világosabbá tenni. Képzeljünk el egy játékot, amelynek (felhasználó által látható) tárgyai, űrhajói és aszteroidái vannak. Két objektum ütközésekor előfordulhat, hogy a programnak különböző dolgokat kell tennie attól függően, hogy mi éppen eltalálta.

Beépített többszörös metódusfeloldással rendelkező nyelvek

C # A C# a 4. verzióbanGroovy - Multi-methods (2010. április) vezette be a dinamikus többmódusú metódusok támogatását a 'dynamic' kulcsszóval. A következő példa a 8-as verzióban (2019 szeptember) bevezetett switch-kifejezésekkel együtt mutatja be a multimódszereket. Sok más statikusan gépelt nyelvhez hasonlóan a C# is támogatja a statikus metódus-túlterhelést.@arrows/multimethod Multiple dispatch in JavaScript/TypeScript with configurable dispatch resolution by Maciej Cąderek. A Microsoft arra számít, hogy a fejlesztők a legtöbb esetben a statikus gépelést fogják választani a dinamikus gépeléssel szemben. A 'dynamic' kulcsszó támogatja a COM-objektumokkal és a dinamikusan gépelt .NET-nyelvekkel való átjárhatóságot.class Program

static void Main()

Console.WriteLine(Utkozteto.osszeutkozes(new Aszteroida(101), new Urhajo(300))); Console.WriteLine(Utkozteto.osszeutkozes(new Aszteroida(10), new Urhajo(10))); Console.WriteLine(Utkozteto.osszeutkozes(new Urhajo(101), new Urhajo(10)));

static class Utkozteto

public static string osszeutkozes(UrTargy x, UrTargy y) => ((x.Meret > 100) && (y.Meret > 100)) ? "Nagy bumm!" : CollideWith(x as dynamic, y as dynamic); private static string CollideWith(Aszteroida x, Aszteroida y) => "a/a"; private static string CollideWith(Aszteroida x, Urhajo y) => "a/s"; private static string CollideWith(Urhajo x, Aszteroida y) => "s/a"; private static string CollideWith(Urhajo x, Urhajo y) => "s/s";

abstract class UrTargy

public UrTargy(int size) => Meret = size;

public int Meret { get; }

class Aszteroida : UrTargy

public Aszteroida(int meret) : base(meret) { }

class Urhajo : UrTargy

public Urhajo(int meret) : base(meret) { } }Kimenet:Nagybumm a/s s/s

Groovy A Groovy egy általános célú, Java kompatibilis/kölcsönösen használható JVM nyelv, amely a Javával ellentétben késői kötést / többszörös diszpatchet használ.multimethods.py , Multiple dispatch in Python with configurable dispatch resolution by David Mertz, et al. /* A fenti C# példa Groovy implementációja A késői kötés ugyanúgy működik, ha nem statikus metódusokat használunk, vagy ha statikusan fordítjuk az osztályokat/módszereket. (@CompileStatic annotáció) */ class Program

static void main(String[] args)

println Utkozteto.osszeutkozes(new Aszteroida(101), new Urhajo(300)) println Utkozteto.osszeutkozes(new Aszteroida(10), new Urhajo(10)) println Utkozteto.osszeutkozes(new Urhajo(101), new Urhajo(10))

class Collider

static String osszeutkozes(UrTargy x, UrTargy y)

(x.meret > 100 && y.meret > 100) ? "Nagy bumm" : collideWith(x, y) // Dinamikus küldés a collideWith módszerhez

private static String collideWith(Aszteroida x, Aszteroida y) { "a/a" } private static String collideWith(Aszteroida x, Urhajo y) { "a/s" } private static String collideWith(Urhajo x, Aszteroida y) { "s/a" } private static String collideWith(Urhajo x, Urhajo y) { "s/s"}

class UrTargy

int meret UrTargy(int size) { this.meret = size }

@InheritConstructors class Aszteroida extends UrTargy { } @InheritConstructors class Urhajo extends UrTargy { }

Common lisp Egy többszörös metódusfeloldással rendelkező nyelvben, mint például a Common Lisp, ez inkább így nézhet ki (Common Lisp példa látható): (defmethod collide-with((x aszteroida) (y aszteroida)) ;; az aszteroidával való ütközéssel foglalkozik ) (defmethod collide-with((x aszteroida) (y urhajo)) ;; az űrhajóba csapódó aszteroidával foglalkozik ) (defmethod collide-with((x urhajo) (y aszteroida)) ;; aszteroidába csapódó űrhajóval foglalkozik. ) (defmethod collide-with((x urhajo) (y urhajo)) ;; űrhajóval ütköző űrhajóval foglalkozik. )és hasonlóan a többi módszer esetében is. Explicit tesztelés és "dinamikus kiosztás" nem használatos.

A többszörös metódusfeloldás mellett a hagyományos elképzelés, miszerint a metódusok osztályokban definiáltak és objektumokban vannak, kevésbé tetszetős - minden egyes fenti, ütközéses metódus két különböző osztályhoz tartozik, nem pedig egyhez. Ezért a metódushívás speciális szintaxisa általában eltűnik, így a metódushívás pontosan úgy néz ki, mint a szokásos függvényhívás, és a metódusok nem osztályokba, hanem általános függvényekbe vannak csoportosítva.

Julia A Julia beépített többszörös metódusfeloldással rendelkezik, és ez a nyelv tervezésének központi eleme. A fenti példa Julia változataként a következőképpen nézhet ki:(collide_with(x::Aszteroida, y::Aszteroida) = ... # az aszteroidával való ütközéssel foglalkozik. collide_with(x::Aszteroida, y::Urhajo) = ... # az űrhajóba csapódó aszteroidával foglalkozik. collide_with(x::Urhajo, y::Aszteroida) = ... # aszteroidába csapódó űrhajóval foglalkozik. collide_with(x::Urhajo, y::Urhajo) = ... # űrhajóval ütköző űrhajóval foglalkozik

Next Generation Shell A Next Generation Shell beépített többszörös metódusfeloldással és predikátummetódus-feloldással rendelkezik, és ezek központi szerepet játszanak a nyelv tervezésében.

Az azonos nevű metódusok többszörös metódusfeloldású metódust alkotnak, ezért nincs szükség külön deklarációra.