Menu
Coddy logo textTech

Den Konstruktor erweitern

Teil des Abschnitts Object Oriented Programming der Lua-Journey von Coddy — Lektion 33 von 70.

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

Die Technik besteht darin, den Namen der Elternklasse zu verwenden, um deren :new()-Methode aufzurufen, wobei die Elternklasse als erstes Argument übergeben wird. Dies können Sie mit der Punkt-Syntax tun: Vehicle.new(Vehicle, speed), oder gleichwertig mit der Doppelpunkt-Syntax: Vehicle:new(speed) — beide sind gültig und führen zum gleichen Ergebnis. Dies erstellt das Basisobjekt mit der gesamten Initialisierung der Elternklasse, das Sie anschließend für die Kindklasse 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)  -- Eltern-Konstruktor aufrufen
    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) (oder gleichbedeutend Vehicle:new(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.

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 im Parent, während jedes Child nur seine eindeutigen 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 auf drei Dateien verteilen:

  • 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. Der :new(name, wingspan)-Konstruktor Ihres Birds 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 zeigen, 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 dann nur das hinzu, was für Vögel einzigartig ist. Dies hält Ihren Code DRY, während eine ordnungsgemäße Vererbungskette aufgebaut wird!

Spickzettel

Um einen Konstruktor der Elternklasse aus einer Kindklasse aufzurufen, verwenden Sie den Namen der Elternklasse mit der Punkt-Syntax und übergeben Sie die Elternklasse selbst als erstes Argument:

local obj = ParentClass.new(ParentClass, parentArgs)

Nachdem Sie den Eltern-Konstruktor aufgerufen haben, fügen Sie kindspezifische Felder hinzu und verknüpfen Sie die Metatabelle erneut mit der Kindklasse:

local Vehicle = {}
Vehicle.__index = Vehicle

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

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

function Car:new(speed, brand)
    local obj = Vehicle.new(Vehicle, speed)  -- Eltern-Konstruktor aufrufen
    obj.brand = brand                         -- Kindspezifisches Feld hinzufügen
    setmetatable(obj, Car)                    -- Erneut mit Car verknüpfen
    return obj
end

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

Dieses Muster hält die Initialisierungslogik DRY – das gemeinsame Setup befindet sich in der Elternklasse, während Kindklassen nur ihre einzigartigen Attribute verarbeiten.

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 Namen und der Flügelspannweite

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

-- TODO: Rufe :info() für den Vogel 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 Object Oriented Programming