Menu
Coddy logo textTech

Den Konstruktor erweitern

Teil des Abschnitts Objektorientierte Programmierung der Lua-Journey von Coddy — Lektion 33 von 70.

Wenn eine Kindklasse von einer Elternklasse erbt, möchten Sie oft, dass der Konstruktor der Kindklasse alles initialisiert, was auch die Elternklasse initialisieren würde – plus alle zusätzlichen Felder, die spezifisch für die Kindklasse sind. Anstatt Code zu duplizieren, können Sie den Konstruktor der Elternklasse direkt aufrufen.

Die Technik beinhaltet die Verwendung des Namens der Elternklasse, um deren Methode :new() aufzurufen, wobei self als erstes Argument übergeben wird. Dies erstellt das Basisobjekt mit der gesamten Initialisierung der Elternklasse, welches Sie dann für das Kind anpassen.

local Vehicle = {}
Vehicle.__index = Vehicle

function Vehicle:new(speed)
    local obj = {speed = speed}
    setmetatable(obj, Vehicle)
    return obj
end

-- Car erbt von Vehicle
local Car = {}
Car.__index = Car
setmetatable(Car, {__index = Vehicle})

function Car:new(speed, brand)
    local obj = Vehicle.new(Vehicle, speed)  -- Aufruf des Eltern-Konstruktors
    obj.brand = brand                         -- Kind-spezifisches Feld hinzufügen
    setmetatable(obj, Car)                    -- Erneut mit Car verknüpfen
    return obj
end

Beachten Sie die wichtigsten Schritte im Konstruktor der Kindklasse: Zuerst rufen wir Vehicle.new(Vehicle, speed) auf, um ein Objekt zu erhalten, bei dem das speed-Feld bereits gesetzt ist. Dann fügen wir das für die Kindklasse spezifische brand-Feld hinzu. Schließlich ändern wir die Metatabelle von Vehicle zu Car, damit die Instanz Methoden zuerst in der Kindklasse nachschlägt.

Sie können beim Aufruf des übergeordneten Konstruktors auch die Doppelpunkt-Syntax verwenden — Vehicle:new(speed) entspricht Vehicle.new(Vehicle, speed), da beide Vehicle als erstes Argument übergeben. Beide Formen funktionieren hier korrekt, also verwenden Sie ruhig diejenige, die Sie für lesbarer halten.

local myCar = Car:new(120, "Toyota")
print(myCar.speed)  -- Ausgabe: 120
print(myCar.brand)  -- Ausgabe: Toyota

Dieses Muster hält Ihren Code DRY – die gemeinsame Initialisierungslogik befindet sich in der Elternklasse, während jede Kindklasse nur ihre spezifischen Attribute verarbeitet.

challenge icon

Aufgabe

Einfach

Lassen Sie uns eine Kreaturenhierarchie aufbauen, bei der eine Kindklasse den Konstruktor ihrer Elternklasse erweitert! Sie werden eine Elternklasse Animal und eine Kindklasse Bird erstellen, wobei der Konstruktor von Bird den Konstruktor von Animal aufruft, um die gemeinsame Initialisierung zu übernehmen, bevor er sein eigenes, einzigartiges Attribut hinzufügt.

Sie werden Ihren Code über drei Dateien organisieren:

  • Animal.lua: Definieren Sie Ihre Elternklasse mit einem :new(name) Konstruktor, der den Namen des Tieres speichert. Fügen Sie eine :describe() Methode hinzu, die den Namen des Tieres ausgibt. Richten Sie das Standard-Klassenmuster mit __index ein und geben Sie die Klasse zurück.
  • Bird.lua: Definieren Sie Ihre Kindklasse, die von Animal erbt. Ihr Bird-Konstruktor :new(name, wingspan) sollte den Elternkonstruktor mit Animal.new(Animal, name) aufrufen, um den Namen zu initialisieren, dann das Feld wingspan zum Objekt hinzufügen und schließlich die Metatabelle wieder mit Bird verknüpfen. Fügen Sie eine :info() Methode hinzu, die sowohl den Namen als auch die Flügelspannweite in einem bestimmten Format ausgibt.
  • main.lua: Binden Sie Ihr Bird-Modul ein, lesen Sie zwei Eingaben (Name und Flügelspannweite), erstellen Sie eine Bird-Instanz und rufen Sie beide Methoden auf, um zu demonstrieren, dass geerbte und kindspezifische Funktionalität zusammenarbeiten.

Sie erhalten zwei Eingaben:

  1. Der Name des Vogels
  2. Die Flügelspannweite des Vogels (eine Zahl)

Erstellen Sie in Ihrer Hauptdatei einen Bird mit dem angegebenen Namen und der Flügelspannweite. Rufen Sie dann :describe() gefolgt von :info() in separaten Zeilen auf.

Die Methode :describe() (geerbt von Animal) sollte Folgendes ausgeben:

Animal: {name}

Die Methode :info() (definiert in Bird) sollte Folgendes ausgeben:

{name} has a wingspan of {wingspan}

Wenn die Eingaben beispielsweise Eagle und 200 sind, sollte die Ausgabe wie folgt aussehen:

Animal: Eagle
Eagle has a wingspan of 200

Der Schlüssel hierbei ist, dass Ihr Bird-Konstruktor die Namensinitialisierung nicht dupliziert – er delegiert diese Arbeit an den Konstruktor von Animal und fügt nur das hinzu, was für Vögel einzigartig ist. Dies hält Ihren Code DRY, während Sie eine ordnungsgemäße Vererbungskette aufbauen!

Probier es selbst

-- main.lua: Einstiegspunkt

local Bird = require('Bird')

-- Eingaben lesen
local name = io.read()
local wingspan = tonumber(io.read())

-- TODO: Erstelle eine Bird-Instanz mit dem angegebenen name und wingspan

-- TODO: Rufe :describe() für den bird auf (geerbt von Animal)

-- TODO: Rufe :info() für den bird auf (definiert in Bird)
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung