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

-- Define the Person class
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

-- Define the Rectangle class
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

-- Define the Rectangle class
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.

-- Define the Rectangle class
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.

-- Define the Animal class
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.

  • Overridingspeakmethod: 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

-- Define a "class" (actually a table)
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

-- Define a "class" (actually a table)
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

-- Only expose the interface, not the implementation details
function Person:showInfo()
    print("Name: " .. self.name)
    print("Age: " .. self.age)
end
Other extensions