29. Inheritance in depth — Homework solutions

Solution .lua files: exercises/29/homework/solutions/.

Problem 1 — Vehicle and Car

Problem. A Car that inherits describe from Vehicle.

Worked solution.

local Vehicle = {}
Vehicle.__index = Vehicle
function Vehicle.new(name)
    return setmetatable({ name = name }, Vehicle)
end
function Vehicle:describe()
    print(self.name .. " is a vehicle.")
end

local Car = setmetatable({}, { __index = Vehicle })
Car.__index = Car
function Car.new(name)
    return setmetatable(Vehicle.new(name), Car)
end

Car.new("Beetle"):describe()   -- Beetle is a vehicle.  (inherited)

Problem 2 — Override

Problem. Car:describe() says "car" instead of "vehicle".

Worked solution. Add to the classes above:

function Car:describe()
    print(self.name .. " is a car.")
end

Vehicle.new("Cart"):describe()   -- Cart is a vehicle.
Car.new("Beetle"):describe()     -- Beetle is a car.

A Vehicle finds the original; a Car finds its own first.

Problem 3 — Extend the parent

Problem. Car:describe() runs the parent's version, then adds a line.

Worked solution.

function Car:describe()
    Vehicle.describe(self)
    print("  It has four wheels.")
end

Car.new("Beetle"):describe()
-- Beetle is a vehicle.
--   It has four wheels.

Common mistakes.

  • Calling the parent with self:describe() inside describe calls this same method again and loops forever. Use the dot form on the parent class: Vehicle.describe(self).

Challenge — Two children

Problem. Bird and Fish both inherit Animal and override move.

Worked solution.

local Animal = {}
Animal.__index = Animal
function Animal.new(name)
    return setmetatable({ name = name }, Animal)
end
function Animal:move()
    print(self.name .. " moves.")
end

local function child()      -- a tiny helper: a class inheriting Animal
    local C = setmetatable({}, { __index = Animal })
    C.__index = C
    function C.new(name) return setmetatable(Animal.new(name), C) end
    return C
end

local Bird = child()
function Bird:move() print(self.name .. " flies.") end

local Fish = child()
function Fish:move() print(self.name .. " swims.") end

local creatures = { Bird.new("Robin"), Fish.new("Nemo") }
for _, c in ipairs(creatures) do
    c:move()
end
-- Robin flies.
-- Nemo swims.

Putting the inherit-from-Animal setup in one helper keeps both children short, and each still overrides move its own way.

Done?

You can share behaviour with a base class and specialise it in children — inheriting, overriding, and extending. The last chapter of Part 6 — Designing a small class — asks how to make a class pleasant to use.