Skip to content

Classes and Inheritance in Lua

Mikkel edited this page Apr 30, 2018 · 2 revisions

Emulating classes

The following code snippet shows how classes can be emulated in Lua:

local Point2D = {}                             -- define `Point2D` as a new "class"
Point2D.__index = Point2D                      -- allow "instances" to access the methods of the class

function Point2D.new(class, x, y)              -- constructor for `Point2D` (note the first parameter, `class`, referring to the class itself)
  local self = setmetatable({}, class)         -- create a new "instance" of the class, called `self`
  self._x = x or 0                             -- set the fields of the instance
  self._y = y or 0                             -- "private" variables are often indicated using a preceding `_`
  return self                                  -- remember to return the new instance
end

function Point2D:getX()                        -- instance method (note the use of `:`)
  return self._x
end

function Point2D:getY()                        -- instance method (note the use of `:`)
  return self._y
end

To make new instances of Point2D, all you have to do is call Point2D:new(x, y), as the following snippet demonstrates:

local point = Point2D:new(10, 20)
print( point:getX(), point:getY() )            -- prints:  10   20

To use a class as a module, simply return it from it's file. You can then use

local Point2D = require "path.to.Point2D"

to import/include it in other files.

A small note on the use of : in Lua

The syntax

  function Point2D:getX()
    -- ...
  end

implicitly adds self as the first parameter, i.e. it's shorthand for

  function Point2D.getX(self)
    -- ...
  end

Similarly, the syntax

  point:getX()

implicitly passes point as the first argument, i.e. it's shorthand for

  point.getX(point)

Emulating inheritance

Inheritance can also be emulated, as the following snippet demonstrates:

local Position = {}                            -- define `Position` as a new class
Position.__index = Position                    -- allow instances to access the class methods

setmetatable(Position, {__index = Point2D})    -- make `Position` "inherit" from `Point2D`

function Position:move(dx, dy)                 -- instance method (note the use of `:`)
  self._x = self._x + (dx or 0)
  self._y = self._y + (dy or 0)
end

Notice how we haven't defined a new constructor for Position? When making classes as described here, subclasses automatically inherit the constructor of their parent class, unless a custom constructor is provided.

We can create new instances of Position using Position:new(x, y), as shown in the example below:

local pos = Position:new(10, 20)
print( pos:getX(), pos:getY() )                -- prints:  10   20
pos:move(5, -5)
print( pos:getX(), pos:getY() )                -- prints:  15   15

Method/constructor overriding

In the example above we didn't need to define a new constructor, but sometimes we do. This is done simply be defining a new constructor on the subclass. Doing so will "overshadow" the parent constructor, however it's often useful to still be able to call the parent constructor from within the subclass constructor.

The following snippet shows how this can be achieved in Lua:

local Point3D = {}                             -- define `Point3D` as a new class
Point3D.__index = Point3D                      -- allow instances to access the methods of the class

setmetatable(Point3D, {__index = Point2D})     -- make `Point3D` inherit from `Point2D`

function Point3D.new(class, x, y, z)           -- constructor for `Point3D` (note the first parameter, `class`, referring to the class itself)
  local self = Point2D:new(x, y)               -- invoke the parent constructor
  setmetatable(self, class)                    -- make it an instance of this class
  self._z = z or 0                             -- add the remaining fields
  return self                                  -- remember to return the new instance
end

function Point3D:getZ()                        -- instance method (note the use of `:`)
  return self._z
end

The same technique can be used to override methods.

We can create new instances of Point3D using Point3D:new(x, y, z), as shown in the example below:

local p3 = Point3D:new(10, 20, 30)
print( p3:getX(), p3:getY(), p3:getZ() )       -- prints:  10   20   30

Limitations of Classes in Lua

The only real limitation of emulating classes in Lua is the lack of method overloading, which is an unfortunate a natural consequence of Lua being a dynamically typed language. Methods to overcome this limitation does exist, but these are both to complex and to context specific to be covered in this tutorial.

Clone this wiki locally