Lua Object-Oriented
Object-Oriented Programming (OOP) is a very popular computer programming architecture that designs applications by creating and manipulating objects.
The following programming languages all support object-oriented programming:
- C++
- Java
- Objective-C
- Smalltalk
- C#
- Ruby
Lua is a lightweight scripting language. Although it does not have built-in powerful object-oriented (OO) features like Java or C++, it is very flexible and can implement object-oriented programming through some techniques.
Object-Oriented Features
Encapsulation: Bind data and methods together, hide implementation details, expose only necessary interfaces, and improve security and maintainability.
Inheritance: Reuse and extend existing code by deriving new classes, reduce repetitive coding, and improve development efficiency and extensibility.
Polymorphism: The same operation behaves differently on different objects, supports unified interface calls, and enhances flexibility and extensibility.
Abstraction: Simplify complex problems by defining core classes and interfaces, hiding unnecessary details to facilitate complexity management.
Object-Oriented in Lua
We know that objects consist of properties and methods.
Classes in Lua can betable + functionsimulated.
As for inheritance, it can be simulated through metatables (not recommended; simulating only the most basic objects is sufficient for most implementations).
In Lua, the most basic structure is the table; we can use tables to create objects.
ClassName = {} -- 创建一个表作为类
Create objects through the new method (or other names) and initialize the object's properties.
function ClassName:new(...)
local obj = {} -- 创建一个新的空表作为对象
setmetatable(obj, self) -- 设置元表,使对象继承类的方法
self.__index = self -- 设置索引元方法
-- 初始化对象的属性
obj:init(...) -- 可选:调用初始化函数
return obj
end
A table is the most basic composite data type in Lua and can be used to represent object properties.
The function in Lua can be used to represent methods:
function ClassName:sayHello()
print("Hello, my name is " .. self.name)
end
Use the new method to create objects, and call the class's methods through the object.
local obj = ClassName:new("Alice") -- 创建对象
obj:sayHello() -- 调用对象的方法
In Lua, a table can be viewed as a variant of an object. Like objects, tables have state (member variables) and can represent independent entities.
Tables not only have data members, but can also contain member functions similar to object methods:
Example
Person = {name = "", age = 0}
-- Person's constructor
function Person:new(name, age)
local obj = {} -- Create a new table as the object
setmetatable(obj, self) -- Set the metatable to make it an instance of Person
self.__index = self -- Set the index metamethod to point to Person
obj.name = name
obj.age = age
return obj
end
-- Add a method: print personal information
function Person:introduce()
print("My name is " .. self.name .. " and I am " .. self.age .. " years old.")
end
Code explanation:
- Personis a table with two properties: name and age; these two properties are the class's default properties.
- Person:new(name, age)is a constructor used to create new Person objects.
- local obj = {}Create a new table as the object; setmetatable(obj, self) sets the metatable so that the table becomes an instance of the Person class.
- self.__index = selfSet the index metamethod so that obj can access the properties and methods of the Person class.
- introduceis a method of the Person class that prints the name and age of the Person object.
Calling methods:
-- 创建一个 Person 对象
local person1 = Person:new("Alice", 30)
-- 调用对象的方法
person1:introduce() -- 输出 "My name is Alice and I am 30 years old."
A Simple Example
The following simple class contains three properties: area, length, and breadth. The printArea method is used to print the calculation result:
Example
Rectangle = {area = 0, length = 0, breadth = 0}
-- Constructor for creating Rectangle objects
function Rectangle:new(o, length, breadth)
o = o or {} -- If no object is passed, create a new empty table
setmetatable(o, self) -- Set the metatable so it inherits Rectangle's methods
self.__index = self -- Ensure methods and properties can be found when accessed
o.length = length or 0 -- Set length, default to 0
o.breadth = breadth or 0 -- Set breadth, default to 0
o.area = o.length * o.breadth -- Calculate area
return o
end
-- Print the area of the rectangle
function Rectangle:printArea()
print("The area of the rectangle is ", self.area)
end
Creating Objects
Creating an object is the process of allocating memory for an instance of a class; each class has its own memory and shares public data:
r = Rectangle:new(nil,10,20)
Accessing Properties
We can use the dot.to access the class's properties:
print(r.length)
Accessing Member Functions
We can use the colon:to access the class's member functions:
r:printArea()
Memory is allocated when objects are initialized.
Complete Example
Below we demonstrate a complete example of Lua object-oriented programming:
Example
Rectangle = {area = 0, length = 0, breadth = 0}
-- Constructor for creating Rectangle objects
function Rectangle:new(o, length, breadth)
o = o or {} -- If no object is passed, create a new empty table
setmetatable(o, self) -- Set the metatable so it inherits Rectangle's methods
self.__index = self -- Ensure methods and properties can be found when accessed
o.length = length or 0 -- Set length, default to 0
o.breadth = breadth or 0 -- Set breadth, default to 0
o.area = o.length * o.breadth -- Calculate area
return o
end
-- Print the area of the rectangle
function Rectangle:printArea()
print("The area of the rectangle is ", self.area)
end
-- Run the example:
local rect1 = Rectangle:new(nil, 5, 10) -- Create a rectangle with length 5 and width 10
rect1:printArea() -- Output "The area of the rectangle is 50"
local rect2 = Rectangle:new(nil, 7, 3) -- Create a rectangle with length 7 and width 3
rect2:printArea() -- Output "The area of the rectangle is 21"
Executing the above program produces the following output:
矩形面积为 50 矩形面积为 21
Lua Inheritance
Inheritance means that an object directly uses the properties and methods of another object, and can be used to extend the properties and methods of a base class.
Inheritance in Lua is implemented by setting the metatable of the subclass.
We can create a new table and set its metatable to the parent class.
In the following example, the Square class will inherit the properties and methods of the Rectangle class and make modifications on that basis.
Rectangle = {area = 0, length = 0, breadth = 0}
-- Constructor for creating Rectangle objects
function Rectangle:new(o, length, breadth)
o = o or {} -- If no object is passed, create a new empty table
setmetatable(o, self) -- Set the metatable so it inherits Rectangle's methods
self.__index = self -- Ensure methods and properties can be found when accessed
o.length = length or 0 -- Set length, default to 0
o.breadth = breadth or 0 -- Set breadth, default to 0
o.area = o.length * o.breadth -- Calculate area
return o
end
-- Print the area of the rectangle
function Rectangle:printArea()
print("The area of the rectangle is ", self.area)
end
-- Define the Square class, inheriting from the Rectangle class
Square = Rectangle:new() -- Square inherits the Rectangle class
-- Override the constructor (all sides of a square are equal)
function Square:new(o, side)
o = o or {} -- If no object is passed, create a new empty table
setmetatable(o, self) -- Set the metatable so it inherits Rectangle's methods
self.__index = self -- Ensure methods and properties can be found when accessed
o.length = side or 0 -- Set the side length
o.breadth = side or 0 -- The width and length of the square are equal
o.area = o.length * o.breadth -- Calculate area
return o
end
-- Run the example:
local rect = Rectangle:new(nil, 5, 10) -- Create a rectangle with length 5 and width 10
rect:printArea() -- Output "The area of the rectangle is 50"
local square = Square:new(nil, 4) -- Create a square with side length 4
square:printArea() -- Output "The area of the rectangle is 16"
RectangleClass: is still the basic Rectangle class, havinglength、breadthandareaproperties, as well as methods to calculate and print the area.
SquareThe class inherits fromRectangle:SquareThe class usesRectangle:new()to inheritRectanglemethods and properties of the class. Since the length and width of a square are equal, weSquare:newoverrode the constructor in the method, settinglengthandbreadthset to the same value (i.e.side)。
Overriding the constructor:Square:new(o, side)When the method creates a Square object, it uses the passed side lengthsideto initializelengthandbreadthproperties, and calculates the area.
Running result:
矩形面积为 50 矩形面积为 16
Function Overriding
In Lua, function overriding (also called method overriding) refers to the redefinition or replacement of an existing method from the parent class by the subclass during inheritance.
A subclass can modify or extend the behavior of parent class methods as needed.
In the above example, the Square class overrides the Rectangle class's constructor, thereby changing the way objects are initialized, especially setting the rectangle's length and breadth to the same value, because the characteristic of a square is equal side lengths.
Next, we use an Animal class and a Dog class that inherits from it to demonstrate how to override methods.
Animal = {name = "Unknown"}
-- Animal class constructor
function Animal:new(o, name)
o = o or {} -- If no object is passed, create a new empty table
setmetatable(o, self) -- Set the metatable so that it inherits Animal's methods
self.__index = self -- Allow the object to access Animal's methods
o.name = name or "Unknown" -- Set the name, default is "Unknown"
return o
end
-- Animal class method: make sound
function Animal:speak()
print(self.name .. " makes a sound.")
end
-- Define the Dog class, inheriting from Animal
Dog = Animal:new() -- Dog inherits from the Animal class
-- Override the Dog class constructor
function Dog:new(o, name, breed)
o = o or {} -- If no object is passed, create a new empty table
setmetatable(o, self) -- Set the metatable so that it inherits methods from Dog and Animal
self.__index = self -- Allow the object to access Dog's methods
o.name = name or "Unknown"
o.breed = breed or "Unknown"
return o
end
-- Override the Dog class's sound method (overriding Animal's speak method)
function Dog:speak()
print(self.name .. " barks.")
end
-- Create an Animal object
local animal = Animal:new(nil, "Generic Animal")
animal:speak() -- Output "Generic Animal makes a sound."
-- Create a Dog object
local dog = Dog:new(nil, "Buddy", "Golden Retriever")
dog:speak() -- Output "Buddy barks."
AnimalClass: defines a base classAnimal, which hasnameproperties andspeakmethods.speakThe method is a default implementation that outputs "some animal makes a sound."DogClass inheritanceAnimal:DogThe class inherits fromAnimal, and through theDog:new()method creates its own instances.Overriding
speakmethod: in theDogclass, overrode thespeakmethod, changing its behavior from the parent class's "make sound" to "dog barking". This is a manifestation of method overriding. The subclass (Dog) changed the parent class (Animal) method's behavior.
Run result:
Generic Animal makes a sound. Buddy barks.
Polymorphism
Lua's polymorphism is implemented through metatables and method overriding. When objects of different types call the same method, Lua executes different methods based on the actual type of the object.
Example
Person = {}
-- Add a constructor to the "class"
function Person:new(name, age)
local obj = {} -- Create a new table as the object
setmetatable(obj, self) -- Set the metatable to indicate it is an instance of the Person class
self.__index = self -- Set the index metamethod to point to Person
obj.name = name
obj.age = age
return obj
end
-- Add methods
function Person:greet()
print("Hello, my name is " .. self.name)
end
-- Define a subclass Student that inherits from Person
Student = Person:new()
-- The subclass overrides the parent class's method
function Student:greet()
print("Hi, I'm a student and my name is " .. self.name)
end
local person2 = Person:new("Charlie", 25)
local student2 = Student:new("David", 18)
-- Polymorphism: objects of different types call the same method
person2:greet() -- Output "Hello, my name is Charlie"
student2:greet() -- Output "Hi, I'm a student and my name is David"
Although person2 and student2 call the same greet method, Lua calls the appropriate version for each because their types are different.
Run result:
Hello, my name is Charlie Hi, I'm a student and my name is David
Other Object-Oriented Concepts
Encapsulation
Encapsulation is usually implemented by encapsulating data and methods in a table. We can simulate encapsulation by controlling access to the table, for example using metamethods to restrict external access.
Example
Person = {}
-- Add encapsulation: hide properties
function Person:new(name, age)
local obj = {}
setmetatable(obj, self)
self.__index = self
obj.name = name
obj.age = age
return obj
end
function Person:setName(name)
self.name = name -- Provide a method to modify name
end
function Person:getName()
return self.name -- Provide a method to get name
end
In this way, we can control access to properties and simulate encapsulation.
Abstraction
Abstraction refers to simplifying complex things by hiding unnecessary details. Although Lua itself does not have the concept of classes, we can achieve abstraction through encapsulation.
Example
function Person:showInfo()
print("Name: " .. self.name)
print("Age: " .. self.age)
end