Vererbung
Ein Apfelbaum ist auch eine Pflanze: Er wächst Tag für Tag, und du kannst ihn wie jede andere Pflanze beschreiben. Er hat aber noch etwas: Er trägt Früchte. Du könntest die ganze Klasse Pflanze kopieren und ergänzen, aber dann müsstest du jede Änderung an Pflanzen zweimal machen. Stattdessen kann eine Klasse von einer anderen Klasse erben: Sie bekommt alle Methoden der anderen Klasse, fügt eigene hinzu und ersetzt die, die anders funktionieren sollen:
local Pflanze = {}
Pflanze.__index = Pflanze
function Pflanze.new(name, tage)
local pflanze = {name = name, tage = tage, alter = 0}
setmetatable(pflanze, Pflanze)
return pflanze
end
function Pflanze:wachsen()
self.alter = self.alter + 1
end
function Pflanze:beschreiben()
return self.name .. ", Tag " .. self.alter .. " von " .. self.tage
end
local Baum = {}
Baum.__index = Baum
setmetatable(Baum, {__index = Pflanze})
function Baum.new(name, tage, frucht)
local baum = Pflanze.new(name, tage)
setmetatable(baum, Baum)
baum.frucht = frucht
return baum
end
function Baum:beschreiben()
return Pflanze.beschreiben(self) .. ", trägt " .. self.frucht
end
local weizen = Pflanze.new("Weizen", 2)
local apfel = Baum.new("Apfelbaum", 30, "Äpfel")
weizen:wachsen()
apfel:wachsen()
print(weizen:beschreiben())
print(apfel:beschreiben())Das gibt Folgendes aus:
Weizen, Tag 1 von 2
Apfelbaum, Tag 1 von 30, trägt ÄpfelDas ist neu:
local Baum = {}undBaum.__index = BaummachenBaumzu einer Klasse, genau wiePflanze.setmetatable(Baum, {__index = Pflanze})gibt der KlasseBaumeine Metatabelle: WasBaumnicht hat, sucht Lua inPflanze. Diese Zeile ist die Vererbung.Baumist eine Unterklasse vonPflanze, undPflanzeist seine Elternklasse.Baum.newwiederholt nicht, wasPflanze.newtut. Es ruftPflanze.newauf, um eine Pflanze mit Name, Tagen und Alter zu erzeugen, macht sie dann mitsetmetatable(baum, Baum)zu einem Baum (eine Tabelle hat nur eine Metatabelle, dies ersetzt alsoPflanze) und fügt den eigenen Schlüssel des Baums hinzu,frucht.
Wenn du apfel:wachsen() aufrufst, sucht Lua wachsen Schritt für Schritt: im apfel selbst (nein), in Baum über die Metatabelle des Apfelbaums (nein) und in Pflanze über die Metatabelle von Baum (ja). Es ist eine Kette: apfel → Baum → Pflanze.
Eine Methode überschreiben. Baum hat sein eigenes beschreiben. Lua hört beim ersten beschreiben auf zu suchen, das es findet, und für einen Baum ist das das in Baum. Es ersetzt (überschreibt) also das in Pflanze. Weizen ist eine gewöhnliche Pflanze und benutzt deshalb weiter das beschreiben aus Pflanze. Die Version des Baums muss die Arbeit der Elternklasse nicht wiederholen: Pflanze.beschreiben(self) ruft die Methode der Elternklasse für denselben Baum auf. Beachte den Punkt und das von Hand übergebene self. self:beschreiben() würde das eigene beschreiben des Baums noch einmal aufrufen, das sich immer wieder selbst aufrufen würde, bis das Programm mit stack overflow endet.
Typische Fehler:
setmetatable(Baum, {__index = Pflanze})vergessen: Bäume finden die Methoden der Pflanzen nicht, undapfel:wachsen()endet mitattempt to call a nil value (method 'wachsen').setmetatable(baum, Baum)inBaum.newvergessen: Der neue Baum bleibt eine gewöhnliche Pflanze.apfel:beschreiben()benutzt dann die Version ausPflanzeund sagt nichts über Früchte, und die eigenen Methoden des Baums werden nicht gefunden.- Zu erwarten, dass Metamethoden vererbt werden. Lua sucht
__tostring,__lt,__addund die anderen direkt in der Metatabelle des Objekts, nicht über__index. HatPflanzeein__tostring, werden Bäume weiter alstable: 0x…ausgegeben, bis duBaum.__tostring = Pflanze.__tostringhinzufügst.
So schreibst du es:
Anweisungen
- 1
Beginne unter
Tier:sprecheneine KlasseKuh, die vonTiererbt: Erzeuge die Tabelle, setze ihren__indexund gib ihr eine Metatabelle, deren__indexTierist. - 2
Füge einen Konstruktor
Kuh.new(name)hinzu. Er erzeugt das Tier mitTier.newund dem Laut"muh", macht es mitsetmetatablezu einer Kuh, fügt einen Schlüsselmilchmit dem Wert0hinzu und gibt die Kuh zurück. Füge außerdem eine MethodeKuh:milchGeben(liter)hinzu, dieliterzumilchaddiert. Erzeuge am Ende des Programms Bella mitKuh.new("Bella"), lass sie 8 Liter Milch geben und dann 6 und gib aus, was sie sagt. Das Programm gibt aus:Rex sagt wau Bella sagt muh - 3
Überschreibe
sprechenfür Kühe:Kuh:sprechen()gibt zurück, wasTier.sprechenzurückgibt, gefolgt davon, wie viel Milch die Kuh gegeben hat. Rex ist ein gewöhnliches Tier und sagt deshalb weiter nurwau. Das Programm gibt aus:Rex sagt wau Bella sagt muh und hat 14 Liter Milch gegeben