Prototype-Based OOP in Lua
In a previous post I described Object-Oriented Programming in Lua using a class-like, Java-style pattern. However, Lua’s metatable system makes it straightforward to implement a more natural and lightweight prototype-based model—closer to JavaScript or Self.
Consider the following:
local Animal = { kind = "Unknown", name = "", age = 0 }
function Animal:greet()
print(
"Hello, I'm " .. self.name .. " the " .. self.kind
.. " " .. self.age .. " year(s) old."
)
end
-- Create a new object using Animal as prototype
function Animal:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
-- Usage
local animal = Animal:new({ kind = "Cat", name = "Jess", age = 25 })
animal:greet()
-- OUTPUT:
-- "Hello, I'm Jess the Cat 3 year(3) old.
Inheritance (Prototype Chain)
-- Child prototype
local Cat = Animal:new()
function Cat:greet()
print("Meowww, I am " .. self.name)
end
-- Usage
local bars = Cat:new({ name = "Bars", age = 9 })
bars:greet()
-- OUTPUT:
-- Meowww, I am Bars
Calling Parent Methods (super)
You can call the parent method manually:
function Cat:greet()
-- call parent method
Animal.greet(self)
print("And I am also a cat.")
end
local annaCat = Cat:new({ name = "Anna", kind = "Cat", age = 4 })
annaCat:greet()
-- OUTPUT:
-- Hello, I'm Anna the Cat 4 year(s) old.
-- And I am also a cat.
Static Members
There is no clean way in Lua to create static field, but it can be easily emulated.
local Animal = {
kind = "Unknown",
name = "",
age = 0,
TotalAnimals = 0, -- static field
}
-- Create a new object using Animal as prototype
function Animal:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
Animal.TotalAnimals = Animal.TotalAnimals + 1
return o
end
for i = 1, 10 do
Animal:new({kind = "Mammal", name = "None", age = i})
end
print(Animal.TotalAnimals)
-- OUTPUT: 10
Private fields and methods
There are also no strictly private members in Lua; usually, private things that should not be accessed are prefixed with _ (underscore).
local Animal = {
-- ...
_privateField = "something",
}
local a = Animal:new()
a._privateField -- bad practice, cannot access private fields
Prototype-based OOP is much simpler and much cleaner than imitation of Java-like classes.
