Classes and inheritance with metatables

Lua · Advanced · 14 min

Most Lua codebases (games, plugins, libraries) use this pattern for objects.

Lua has no class keyword. Instead a class is a table of methods, and instances are tables whose metatable has __index pointing at that class. Missing fields (the methods) are found on the class.

The colon syntax wires up self. function Account:deposit(v) defines a function with a hidden first parameter self, and acc:deposit(10) calls Account.deposit(acc, 10). Mixing . and : is the classic bug: acc.deposit(10) passes 10 as self.

A typical constructor is function Class.new(...) that creates a table, sets the metatable and initialises fields. Setting Class.__index = Class lets the class serve as its own metatable.

Inheritance chains lookups: make the subclass's metatable index the parent class (setmetatable(Sub, {__index = Base})). A missing method is searched in the instance, then the subclass, then the base. Call the parent's method explicitly with Base.method(self, ...); there is no super.

Privacy is a convention (an underscore prefix) unless you use closures instead of tables for state. Many projects use a small class library (middleclass, classic), but they are thin wrappers over exactly this mechanism, so it pays to understand it directly.

Example

local Account = {}
Account.__index = Account

function Account.new(owner, balance)
  return setmetatable({owner = owner, balance = balance or 0}, Account)
end
function Account:deposit(v) self.balance = self.balance + v end
function Account:describe() return self.owner .. ": " .. self.balance end

local Savings = setmetatable({}, {__index = Account})
Savings.__index = Savings
function Savings.new(owner, balance, rate)
  local self = Account.new(owner, balance)
  self.rate = rate
  return setmetatable(self, Savings)
end
function Savings:add_interest() self:deposit(self.balance * self.rate) end
function Savings:describe()
  return Account.describe(self) .. " @" .. self.rate   -- call "super"
end

local s = Savings.new("ana", 100, 0.05)
s:add_interest()
print(s:describe())                --> ana: 105.0 @0.05
print(getmetatable(s) == Savings)  --> true

Remember

  • A class is a method table used as the instances' __index
  • obj:m(x) is obj.m(obj, x); define methods with : to get self
  • Inheritance = the subclass table's own __index points at the base

Watch out for

  • Calling a method with a dot (obj.m()), so self is missing or wrong.

Check yourself

  1. What does obj:greet("hi") desugar to?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.lua.org/pil/16.html

Builds on

Unlocks

Nothing builds on this yet.