OOP#

Lua has no class keyword. Object orientation is built from two primitives the operator already has: tables (which hold the state) and metatables (which intercept reads, writes, and operator calls). Method-call sugar (obj:method()) and the __index metamethod give the rest.

For the underlying call surface, see Functions. For the operator-overloading metamethods that fit naturally here, see Operators.

Metatables#

Every table can have a metatable attached with setmetatable(t, mt). The metatable’s fields are metamethods, hooks the runtime calls when certain operations hit the table. The most-used metamethod is __index, which is consulted when a read misses.

local fallback = {name = "default", port = 80}
local config   = {port = 8080}

setmetatable(config, {__index = fallback})

print(config.port)    --> 8080      hit on config
print(config.name)    --> default   miss, looked up in fallback

__index can also be a function; the runtime calls it with (t, key) and uses the returned value.

The full metamethod set covers reads (__index), writes (__newindex), arithmetic (__add, __sub, __mul, __div, __mod, __pow, __unm, __concat), comparison (__eq, __lt, __le), call (__call), length (__len), iteration (__pairs), tostring (__tostring), and garbage collection (__gc).

Objects#

A Lua object is a table whose metatable’s __index points to a class table of shared methods. The class table is itself an ordinary table; methods are just functions that take self as the first argument.

local Point = {}
Point.__index = Point          -- methods live here

function Point.new(x, y)       -- constructor (called as Point.new)
  return setmetatable({x = x, y = y}, Point)
end

function Point:distance(other) -- method (called as p:distance(q))
  local dx, dy = self.x - other.x, self.y - other.y
  return math.sqrt(dx*dx + dy*dy)
end

local p = Point.new(0, 0)
local q = Point.new(3, 4)
print(p:distance(q))           --> 5.0

Two pieces of sugar are at work. p:distance(q) is Point.distance(p, q) after __index resolves the method. function Point:distance(other) is function Point.distance(self, other) because the colon adds self automatically.

Inheritance#

Single inheritance is a one-line chain of __index tables.

local Animal = {}
Animal.__index = Animal

function Animal.new(name)
  return setmetatable({name = name}, Animal)
end

function Animal:speak() print(self.name .. " makes a sound") end

local Dog = setmetatable({}, {__index = Animal})
Dog.__index = Dog

function Dog.new(name) return setmetatable({name = name}, Dog) end
function Dog:speak() print(self.name .. " barks") end

local d = Dog.new("rex")
d:speak()                      --> rex barks

Multiple inheritance is built by giving __index a function that walks several parent tables.

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 class(parents)
  local c = {}
  setmetatable(c, {__index = function(_, k) return search(k, parents) end})
  c.__index = c
  return c
end

Operator overloading#

Arithmetic, concat, comparison, length, and call all dispatch through metamethods. The runtime checks the metatable of the left operand first, then the right.

local Vec = {}
Vec.__index = Vec

function Vec.new(x, y) return setmetatable({x = x, y = y}, Vec) end

function Vec.__add(a, b)  return Vec.new(a.x + b.x, a.y + b.y) end
function Vec.__eq(a, b)   return a.x == b.x and a.y == b.y    end
function Vec.__tostring(v) return ("(%g, %g)"):format(v.x, v.y) end

local a = Vec.new(1, 2)
local b = Vec.new(3, 4)
print(tostring(a + b))      --> (4, 6)
print(a == Vec.new(1, 2))   --> true

References#