Object-Oriented Programming (OOP) is a programming paradigm that uses objects and classes to structure code in a way that promotes reusability, modularity, and abstraction. Lua, a lightweight and embeddable scripting language, does not have built-in support for OOP, but it provides powerful mechanisms such as tables and metatables to implement OOP concepts.
with hands-on learning.
get the skills and confidence to land your next move.
In Lua, tables are the primary data structure and can be used to represent objects. Metatables and metamethods allow tables to exhibit behavior similar to classes and inheritance. This guide will explore the basics of OOP in Lua, including defining classes, creating objects, implementing inheritance, and encapsulation.
Basics of Object-Oriented Programming (OOP) in Lua
Defining a Class
In Lua, a class can be defined using a table, and methods are added as functions within that table. The self parameter represents the instance of the class.
-- Define a class
Person = {}
Person.__index = Person
function Person:new(name, age)
local self = setmetatable({}, Person)
self.name = name
self.age = age
return self
end
function Person:greet()
print("Hello, my name is " .. self.name .. " and I am " .. self.age .. " years old.")
endIn this example, the Person table represents a class. The Person:new function is a constructor that initializes a new instance of the class. The Person:greet function is a method that prints a greeting message.
Creating an Object
To create an object (instance) of a class, you call the constructor method.
-- Create an object
local john = Person:new("John", 30)
john:greet() -- Output: Hello, my name is John and I am 30 years old.In this example, an object john is created using the Person:new constructor, and the john:greet method is called to print a greeting message.
Inheritance and Polymorphism
Implementing Inheritance
Inheritance allows a class to inherit properties and methods from another class. In Lua, this can be achieved by setting the metatable of the subclass to the superclass.
-- Define a subclass
Student = setmetatable({}, {__index = Person})
Student.__index = Student
function Student:new(name, age, grade)
local self = Person:new(name, age)
setmetatable(self, Student)
self.grade = grade
return self
end
function Student:study()
print(self.name .. " is studying.")
endIn this example, the Student class inherits from the Person class. The Student:new constructor initializes a new instance and sets its metatable to Student. The Student:study method is an additional method specific to the Student class.
Method Overriding
Method overriding allows a subclass to provide a specific implementation of a method already defined in its superclass.
function Student:greet()
print("Hello, my name is " .. self.name .. ", I am " .. self.age .. " years old, and I am in grade " .. self.grade .. ".")
end
-- Create an object
local jane = Student:new("Jane", 20, "A")
jane:greet() -- Output: Hello, my name is Jane, I am 20 years old, and I am in grade A.
jane:study() -- Output: Jane is studying.In this example, the Student:greet method overrides the Person:greet method, providing a specific implementation for Student objects.
Encapsulation
Private and Public Members
Encapsulation is the practice of hiding the internal state of an object and only exposing certain methods. In Lua, this can be achieved using local variables and functions within a module.
-- Define a class with encapsulation
BankAccount = {}
BankAccount.__index = BankAccount
function BankAccount:new(balance)
local self = setmetatable({}, BankAccount)
local balance = balance
function self:deposit(amount)
balance = balance + amount
end
function self:withdraw(amount)
if amount <= balance then
balance = balance - amount
else
print("Insufficient funds")
end
end
function self:getBalance()
return balance
end
return self
endIn this example, the BankAccount class encapsulates the balance variable, making it private. The deposit, withdraw, and getBalance methods provide controlled access to the balance.
Using Metatables for Encapsulation
Metatables can also be used to control access to table fields, providing another way to achieve encapsulation.
-- Define a class with metatables for encapsulation
SecureAccount = {}
SecureAccount.__index = SecureAccount
function SecureAccount:new(balance)
local self = setmetatable({}, SecureAccount)
self._balance = balance
return self
end
function SecureAccount:deposit(amount)
self._balance = self._balance + amount
end
function SecureAccount:withdraw(amount)
if amount <= self._balance then
self._balance = self._balance - amount
else
print("Insufficient funds")
end
end
function SecureAccount:getBalance()
return self._balance
end
-- Create an object
local account = SecureAccount:new(100)
account:deposit(50)
account:withdraw(30)
print("Balance:", account:getBalance()) -- Output: Balance: 120In this example, the _balance field is used to store the balance, and methods provide controlled access to it.
Practical Examples
Example: Creating a Simple Class
Let’s create a simple Car class with properties make and model, and a method to display its details.
-- Define a Car class
Car = {}
Car.__index = Car
function Car:new(make, model)
local self = setmetatable({}, Car)
self.make = make
self.model = model
return self
end
function Car:details()
print("Car make: " .. self.make .. ", model: " .. self.model)
end
-- Create an object
local car1 = Car:new("Toyota", "Corolla")
car1:details() -- Output: Car make: Toyota, model: CorollaIn this example, the Car class has properties make and model, and the details method prints the car’s details.
Example: Implementing Inheritance
Let’s create a Truck class that inherits from Car and adds a new property capacity.
-- Define a Truck class inheriting from Car
Truck = setmetatable({}, {__index = Car})
Truck.__index = Truck
function Truck:new(make, model, capacity)
local self = Car:new(make, model)
setmetatable(self, Truck)
self.capacity = capacity
return self
end
function Truck:details()
print("Truck make: " .. self.make .. ", model: " .. self.model .. ", capacity: " .. self.capacity .. " tons")
end
-- Create an object
local truck1 = Truck:new("Ford", "F-150", 3)
truck1:details() -- Output: Truck make: Ford, model: F-150, capacity: 3 tonsIn this example, the Truck class inherits from the Car class and overrides the details method to include the capacity property.
Conclusion
Object-Oriented Programming (OOP) in Lua leverages tables and metatables to implement classes, inheritance, and encapsulation. Although Lua does not have built-in support for OOP, its flexible and powerful mechanisms allow developers to create robust and reusable code. This guide covered the basics of defining classes, creating objects, implementing inheritance and polymorphism, and encapsulating data. Practical examples demonstrated how to apply these concepts in real-world scenarios, making Lua a versatile language for OOP.
Additional Resources
To further your understanding of Lua programming and Object-Oriented Programming, consider exploring the following resources:
- Lua Documentation: The official Lua documentation. Lua Documentation
- Programming in Lua: A comprehensive book on Lua by Roberto Ierusalimschy. Programming in Lua
- Lua Users Wiki: A community-driven resource for Lua programmers. Lua Users Wiki
- LuaRocks: A package manager for Lua modules. LuaRocks
By leveraging these resources, you can deepen your knowledge of Lua and enhance your ability to develop powerful scripts and applications using Object-Oriented Programming principles.




