Konstruktoren
Die Karotte aus der vorigen Übung ist eine einzige Tabelle, von Hand hingeschrieben. Auf einem Hof wachsen aber viele Pflanzen: Karotten, Kartoffeln, Kürbisse … Jede mit all ihren Methoden hinzuschreiben, würde bedeuten, immer wieder denselben Code zu schreiben. Schreibe stattdessen eine Funktion, die sie herstellt. Eine Funktion, die ein neues Objekt erzeugt und zurückgibt, heißt Konstruktor:
local function neuePflanze(name, tage)
local pflanze = {name = name, tage = tage, alter = 0}
function pflanze:wachsen()
self.alter = self.alter + 1
end
function pflanze:beschreiben()
print(self.name .. ": Tag " .. self.alter .. " von " .. self.tage)
end
return pflanze
end
local karotte = neuePflanze("Karotte", 3)
local kuerbis = neuePflanze("Kürbis", 7)
karotte:wachsen()
karotte:wachsen()
kuerbis:wachsen()
karotte:beschreiben()
kuerbis:beschreiben()Das gibt Folgendes aus:
Karotte: Tag 2 von 3
Kürbis: Tag 1 von 7Jeder Aufruf von neuePflanze führt seinen Code erneut aus: Er erzeugt eine neue Tabelle, legt die beiden Methoden hinein und gibt sie zurück. karotte und kuerbis sind zwei getrennte Tabellen, wenn die Karotte wächst, ändert sich der Kürbis also nicht. Und weil die Methoden self benutzen und keinen festen Namen, funktioniert derselbe Code für beide.
In {name = name, tage = tage, alter = 0} kommt dasselbe Wort zweimal vor, das ist in Konstruktoren üblich. Links vom = steht der Schlüssel, rechts der Wert, hier der Parameter mit demselben Namen. neuePflanze("Karotte", 3) erzeugt also {name = "Karotte", tage = 3, alter = 0}.
Der klassische Fehler: return vergessen. Ohne return pflanze erzeugt der Konstruktor die Tabelle, gibt sie aber nicht zurück, und die Variable bekommt nil:
local function neuePflanze(name, tage)
local pflanze = {name = name, tage = tage, alter = 0}
function pflanze:wachsen()
self.alter = self.alter + 1
end
end
local karotte = neuePflanze("Karotte", 3)
karotte:wachsen()Das Programm endet mit einem Fehler:
main.lua:10: attempt to index a nil value (local 'karotte')Der Fehler zeigt auf die Zeile, in der du das Objekt zum ersten Mal benutzt, nicht auf den Konstruktor. Wenn ein neues Objekt nil ist, prüfe das return des Konstruktors.
Ein Problem. Jeder Aufruf von neuePflanze erzeugt auch die Methoden neu: Die Zeilen mit function pflanze:wachsen() laufen erneut und machen eine neue Funktion. Das wachsen der Karotte und das wachsen des Kürbisses tun dasselbe, aber es sind zwei verschiedene Funktionen:
local function neuePflanze(name)
local pflanze = {name = name, alter = 0}
function pflanze:wachsen()
self.alter = self.alter + 1
end
return pflanze
end
local karotte = neuePflanze("Karotte")
local kuerbis = neuePflanze("Kürbis")
print(karotte.wachsen == kuerbis.wachsen)Das gibt Folgendes aus:
falseBei drei Pflanzen spielt das keine Rolle. Aber ein Feld mit 1.000 Pflanzen mit je zehn Methoden würde 10.000 Funktionen enthalten, die Kopien derselben zehn sind. Besser wäre es, die Methoden an einer Stelle zu halten, von allen Pflanzen gemeinsam benutzt, und jede Pflanze nur ihre eigenen Daten halten zu lassen: Name, Tage und Alter. Dafür gibt es Metatabellen, und sie sind das Thema der nächsten Übung.
So schreibst du es:
Anweisungen
- 1
Emma hat mehr Hühner als nur Goldi. Mache aus dem Code, der Goldi erzeugt, einen Konstruktor: eine lokale Funktion
neuesHuhn(name), die ein Huhn mit dem gegebenen Namen und 0 Eiern erzeugt, ihm beide Methoden gibt und es zurückgibt. Erzeuge dann Goldi mitlocal goldi = neuesHuhn("Goldi"). Das Programm gibt dasselbe aus wie vorher. - 2
Erzeuge unter Goldi mit
neuesHuhnein zweites Huhn, Perle. Perle legt 3 Eier und dann noch 1. Gib ihren Bericht unter Goldis aus:Goldi hat 2 Eier gelegt Perle hat 4 Eier gelegt - 3
Prüfe, was diese Übung über Methoden sagt: Gib am Ende des Programms
goldi.legen == perle.legenaus. Das Programm gibt aus:Goldi hat 2 Eier gelegt Perle hat 4 Eier gelegt false