A Lua -nyelvi elemeit tekintve- nem támogatja az objektum-orientált
fejlesztést. Azonban a nyelv beépített tábla típusa felfogható egy objektumként.
Akárcsak az objektumok a táblák is rendelkeznek egy azonosítóval (általában self-nek nevezzük de más nevet is használhatunk) ami független
a tábla tartalmától, két tábla azonos értékekkel különböző objektumnak számít. Továbbá
a táblák is rendelkeznek élettartammal, ami független attól, hogy hol vagy ki által lettek
létrehozva. Akár csak az objektumok a Lua tábla típusa is rendelkezhet saját műveletekkel
A penzKivetel valójában a Szamla tábla egy mezőneve ami egy függvényt tartalmaz.
Amit a követketőképpen hívhatunk meg:
Az egyel fentebb látható kódrészlet penzKivetel mezőjében tárolt függvény, már majdnem megegyezik
egy tagfüggvénnyel, azonban a függvényünk még egy globális változót a Szamla-t
használja a törzsében. Ennek következtében egyrészt a függvény csak azon az objektumon fog működni ahol definiáltuk,
másrészt a ha a Szamla globális változóban tárolt objektum megszűnik, mert kinullázuk, vagy átállítjuk máshova, vagy
új változóban tároljuk az objektumot a függvényünk máris használhatatlanná válik:
A fentebb leírtak ellentmondásba kerülnek azzal az állítással, hogy az objektumok egymástól függetlenül léteznek.
Ahhoz, hogy egy függvény tagfüggvény szerűen viselkedjen át kell adnunk neki
egy még egy paramétert, az objektumot amin a meghívtuk azt.
A paraméter neve tetszőleges, de a self és a this az elterjedt elnevezés. Ha a függvényünket az alábbiak szerint
módosítjuk, akkor már biztonságosan használhatjuk azt, nem függünk egy külső globális változótól.
A fentebb leírt metódus a következőképpen hívható meg:
Ha nem akarjuk a hívó objektumot explicit módon átadni akkor a : operátor segítségével hívva a függvényt automatikusan átadásra kerül.
Az osztályok (class) valójában az objektumok létrehozásának egy speciális formája. Sok objektum orientált nyelv rendelkezik az osztályok koncepciójával. Az ilyen nyelvekben az objektumok osztály példányok. A Lua nem rendelkezik osztály fogalommal, minden objektum magénak definiálja a működését és az alakját. Ennek ellenére lehetséges az osztályokhoz hasonló viselkedés előidézése a Lua-ban (nem minden tudunk azonban megoldani, lásd később virtuális függvények)
A Lua megoldása az osztályokra nagyon hasonlít a prototípus alapú nyelvekéhez (Self, NewtonScript). Az ilyen nyelvekben az objektumok rendelkezhetnek egy prototípussal, ami egy objektum, ahonnan az egyes objektumok a hiányzó műveleteket megtalálják. Ahhoz, hogy ehhez hasonló nyelvekben írjunk egy osztályt, egyszerűen létrehozunk egy objektumot amit a későbbi példányaink felhasználnak.
Ahhoz, hogy ezt megtegyük szükségünk van a metatábla (metatable) __index tagjának használatára.
Ha a Lua-ba megpróbáluk elérni egy olyan táblamezőt ami nem létezik például megpróbálnánk elérni a Szamla tábla
tulajdonos mezőjét, akkor az eredmény nil lesz, különben a metatábla segítségével meghatározzuk az értéket.
Valójában az történik, hogy megnézzük van-e a táblánknak adott nevű mezője és ha nincs, akkor a metatáblájában az __index
tagnak nézzük meg, hogy van-e ilyen tagja. A lenti példakód szemlélteti, hogy hozzunk létre metatáblát és egy "konstruktort".
A metatábla beállítása sor a kulcs, ez a sor gondoskodik arról, hogy a műveleteket és az adattagokat megtaláljuk a prototípusban. Ennek következményeként, ha a prototípus objektumban beállítunk egy mezőnek egy értéket és az újban még nem tettük ezt meg, akkor a prototípus értékét kapjuk vissza ha először használjuk azt a mező.
Mivel az osztályok valójában objektumok, ezért kaphatnak műveleteket más osztályoktól is, ez a
viselkedés használható, mint öröklés. Az örökléshez először rendelkeznünk kell egy ősosztállyal, használjuk erre a célra
a fentebb létrehozott Szamla osztályt. Hozzuk most létre egy olyan számlát aminek a penzKivetel nem egyszerűen levonja az
összeget, hanem megengedi, hogy mínuszba menjünk egy bizonyos határig.
A fentebb látható példa szemlélteti az öröklés menetét. Az ősosztály függvényit felül tudjuk definiálni, hogy a leszármazottban másképp viselkedjenek.
Ahhoz, hogy ezt a viselkedést szimuláljuk rengeteg plusz kód megírására van szükségünk, kezve az alapoktól: Osztályok létrehozása, konstruktorok, virtuális tábla az objektumokhoz. Rengetegfajta osztály implementáció létezik jelenleg a Lua-hoz ami vagy támogatja ezt a viselkedést, vagy csak az elfedést alkalmazza. Az iménti oldalon: https://github.com/jpatte/yaci.lua található egy "könyvtár", ami folyamatosan fejlődik és jelenleg a legtöbbet tudja. A könyvtárról a http://lua-users.org/wiki/YetAnotherClassImplementation oldalon is található egy leírás példákkal. A "könyvtár" használatára egy példa ez a program. A feladat egy másik megvalósítása ami saját osztály implementációt tartalmaz és csak az elfedést teszi lehetővé megtalálható itt.
Mivel az objektumok elég összetettek a Lua-ban, ezért többféleképpen is tudunk objektum orientált viselkedést szimulálni.
A fentebb leírtak, a metatábla használatáról valószínűleg a legegyszerűbb, leghatékonyabb és rugalmasabb megoldás. Azonban
vannak esetek, amikor egy másik megoldás alkalmasabb a céljaink elérésére. A többszörös öröklés eléréséhez egy másik ábrázolásra van szükségünk.
Ennek az implementációnak a kulcsa a metatáblabeli __index használata egy speciális függvényen keresztül. A Lua ha nem talál egy kulcsot (a táblák asszociatív tömbök)
az adott objektumban akkor elkezdi keresni a metatáblában, ahol az __index-en keresztül tetszőleges számú ősben kerestethetjük azt.
A Lua filozófia azt mondja, hogy ha nem tanácsos valamit megváltoztatni, hát ne tegyük. Tehát ha azt akarjuk, hogy bizonyos adattagok kívülről ne legyenek láthatóak, akkor trükköt kell bevetnünk, mert bizony a nyelv ezt nem biztosítja számunkra.
Az ötlet a következő: a példányosítás során egy külön tömbben tároljuk az
adatokat és egy másikban az eljárásokat (interface). A metódusok hívása a
második tömbből lehetséges, amely nem tartalmazza az elsőt. Ennek szemléltetésére nézzük a következő kódrészletet:
Az implementáció kulcsa az, hogy az egyes függvények hívásakor nem kerül átadásra a self hanem, közvetlenül érjük azt el.
Előfordulhat, hogy olyan objektumot szeretnénk létrehozni, aminek csak egyetlen egy metódusa van például funktorok.
Másik érdekes lehetőség egy olyan objektum aminek egyetlen metódusa egy kapcsoló szerű függvény, ami más műveletet hajt
végre a paraméterének függvényében. Egy lehetséges implementáció erre a következő függvény: