Inheritance, Mixins and Class Libraries
18.1 Single Inheritance
local Animal = {}
Animal.__index = Animal
function Animal.new(name) return setmetatable({name = name}, Animal) end
function Animal:speak() return self.name .. " makes a sound" end
function Animal:describe() return "I am " .. self.name end
local Dog = setmetatable({}, {__index = Animal}) -- Dog inherits from Animal
Dog.__index = Dog
function Dog.new(name, breed)
local self = Animal.new(name) -- call the parent constructor
self.breed = breed
return setmetatable(self, Dog)
end
function Dog:speak() -- override
return self.name .. " barks"
end
function Dog:describe()
return Animal.describe(self) .. ", a " .. self.breed -- call the parent method ("super")
end
local d = Dog.new("Rex", "Labrador")
print(d:speak(), d:describe())
print(getmetatable(d) == Dog, getmetatable(Dog).__index == Animal)
local function is_instance(obj, class)
local mt = getmetatable(obj)
while mt do
if mt == class then return true end
local parent = getmetatable(mt)
mt = parent and parent.__index
end
return false
end
print(is_instance(d, Animal)) --> true18.2 A Reusable class() Helper
Metamethods are looked up directly in the metatable (raw access), not via __index. Subclasses must copy them.
local function class(parent)
local c = {}
c.__index = c
if parent then setmetatable(c, {__index = parent}) end
c.super = parent
c.new = function(...)
local instance = setmetatable({}, c)
if instance.init then instance:init(...) end
return instance
end
return c
end
local Shape = class()
function Shape:init(name) self.name = name end
function Shape:area() return 0 end
function Shape:__tostring() return self.name .. " with area " .. self:area() end
local Circle = class(Shape)
function Circle:init(r) Shape.init(self, "circle"); self.r = r end
function Circle:area() return math.pi * self.r ^ 2 end
Circle.__tostring = Shape.__tostring -- metamethods aren't inherited through __index: copy them
print(Circle.new(2))18.3 Multiple Inheritance and Mixins
local function search(k, parents)
for _, p in ipairs(parents) do
local v = p[k]
if v ~= nil then return v end
end
end
local function create_class(...)
local parents = {...}
local c = {}
setmetatable(c, {__index = function(t, k) local v = search(k, parents); t[k] = v; return v end})
c.__index = c
return c
end
local Serializable = {serialize = function(self) return "serialized:" .. tostring(self.id) end}
local Comparable = {equals = function(a, b) return a.id == b.id end}
local function include(class, mixin) for k, v in pairs(mixin) do class[k] = v end return class end
local Model = include(include({}, Serializable), Comparable)18.4 Class Libraries
Popular, full-featured (mixins, metamethod inheritance, isInstanceOf) Tiny class library classic (rxi) Minimal (used often with LÖVE) Penlight pl.class Part of the Penlight utility library From the HUMP library (LÖVE)
local class = require("middleclass")
local Fruit = class("Fruit")
function Fruit:initialize(sweetness) self.sweetness = sweetness end
local Lemon = class("Lemon", Fruit)
function Lemon:initialize() Fruit.initialize(self, 1) end
local lemon = Lemon:new()
print(lemon:isInstanceOf(Fruit), lemon.class.name) --> true Lemon