← Back to the index page

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.

Feedback

For feedback, please check the contacts section. Before writing, please specify where you came from and who you are. Sometimes spammers go insane. Thank you in advance for your understanding.

← Back to the index page