Menu
Coddy logo textTech

Extending the Constructor

Part of the Object Oriented Programming section of Coddy's Lua journey — lesson 33 of 70.

When a child class inherits from a parent, you often want the child's constructor to initialize everything the parent would—plus any additional fields specific to the child. Instead of duplicating code, you can call the parent's constructor directly.

The technique involves using the parent class name to invoke its :new() method, passing the parent class as the first argument. You can do this with dot syntax: Vehicle.new(Vehicle, speed), or equivalently with colon syntax: Vehicle:new(speed) — both are valid and produce the same result. This creates the base object with all parent initialization, which you then customize for the child.

local Vehicle = {}
Vehicle.__index = Vehicle

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

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

function Car:new(speed, brand)
    local obj = Vehicle.new(Vehicle, speed)  -- Call parent constructor
    obj.brand = brand                         -- Add child-specific field
    setmetatable(obj, Car)                    -- Re-link to Car
    return obj
end

Notice the key steps in the child constructor: first, we call Vehicle.new(Vehicle, speed) (or equivalently Vehicle:new(speed)) to get an object with the speed field already set. Then we add the child-specific brand field. Finally, we change the metatable from Vehicle to Car so the instance looks up methods in the child class first.

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

This pattern keeps your code DRY—shared initialization logic lives in the parent, while each child only handles its unique attributes.

challenge icon

Challenge

Easy

Let's build a creature hierarchy where a child class extends its parent's constructor! You'll create an Animal parent class and a Bird child class, where Bird's constructor calls Animal's constructor to handle shared initialization before adding its own unique attribute.

You'll organize your code across three files:

  • Animal.lua: Define your parent class with a :new(name) constructor that stores the animal's name. Include a :describe() method that prints the animal's name. Set up the standard class pattern with __index and return the class.
  • Bird.lua: Define your child class that inherits from Animal. Your Bird's :new(name, wingspan) constructor should call the parent constructor using Animal.new(Animal, name) to initialize the name, then add the wingspan field to the object, and finally re-link the metatable to Bird. Include a :info() method that prints both the name and wingspan in a specific format.
  • main.lua: Require your Bird module, read two inputs (name and wingspan), create a Bird instance, and call both methods to demonstrate that inherited and child-specific functionality work together.

You will receive two inputs:

  1. The bird's name
  2. The bird's wingspan (a number)

In your main file, create a Bird with the given name and wingspan. Then call :describe() followed by :info() on separate lines.

The :describe() method (inherited from Animal) should print:

Animal: {name}

The :info() method (defined in Bird) should print:

{name} has a wingspan of {wingspan}

For example, if the inputs are Eagle and 200, the output should be:

Animal: Eagle
Eagle has a wingspan of 200

The key here is that your Bird constructor doesn't duplicate the name initialization—it delegates that work to Animal's constructor, then adds only what's unique to birds. This keeps your code DRY while building a proper inheritance chain!

Cheat sheet

To call a parent class constructor from a child class, use the parent class name with dot syntax, passing the parent class itself as the first argument:

local obj = ParentClass.new(ParentClass, parentArgs)

After calling the parent constructor, add child-specific fields and re-link the metatable to the child class:

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)  -- Call parent constructor
    obj.brand = brand                         -- Add child-specific field
    setmetatable(obj, Car)                    -- Re-link to Car
    return obj
end

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

This pattern keeps initialization logic DRY—shared setup lives in the parent, while children only handle their unique attributes.

Try it yourself

-- main.lua: Entry point

local Bird = require('Bird')

-- Read inputs
local name = io.read()
local wingspan = tonumber(io.read())

-- TODO: Create a Bird instance with the given name and wingspan

-- TODO: Call :describe() on the bird (inherited from Animal)

-- TODO: Call :info() on the bird (defined in Bird)
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming