Skip to content
Luke100000 edited this page May 2, 2023 · 6 revisions

A subclass can be derived from a class using a reserved method named extend.
Similarly to class(), this method also takes an optional argument name and an optional argument params implemented as a table with named keys.
The new subclass inherits its superclass attributes and methods.

local Window = class ("Window", { width = 150, height = 100})
local Frame = Window:extend("Frame", { color = "black" })

local appFrame = Frame()
print(appFrame.width, appFrame.height, appFrame.color) -- outputs 150, 100, "black"

A subclass has an attribute named super which points to its superclass.

print(Frame.super) -- outputs "class 'Window' (table: 0x0002ceb8)"

A subclass can have its own copies of its superclass attributes and method. Therefore, it can redefine them without affecting the superclass itself. However, a subclass still has free access to his superclass attributes and methods via super.

-- A base class "Window"
local Window = class ("Window", {x = 10, y = 10, width = 150, height = 100})

function Window:init(x, y, width, height)
  Window.set(self, x, y, width, height)
end

function Window:set(x, y, w, h)
  self.x, self.y, self.width, self.height = x, y, w, h
end

-- a "Frame" subclass
local Frame = Window:extend({color = 'black'})
function Frame:init(x, y, width, height, color)
  -- Calling the superclass constructor
  Frame.super.init(self, x, y, width, height)
  -- Setting the extra class member
  self.color = color
end

-- Redefining the set() method
function Frame:set(x,y)
  self.x = x - self.width/2
  self.y = y - self.height/2
end

-- An appFrame from "Frame" class
local appFrame = Frame(100,100,800,600,'red')
print(appFrame.x, appFrame.y) -- outputs 100, 100

-- Calls the new set() method
appFrame:set(400,400)
print(appFrame.x, appFrame.y) -- outputs 0, 100

-- Calls the old set() method in the superclass "Window"
appFrame.super.set(appFrame,400,300)
print(appFrame.x, appFrame.y) -- outputs 400, 300

Also, bear in mind classes are metatables of their subclasses.

local aClass = class("aClass")
local aSubClass= aClass:extend()
print(getmetatable(aSubClass)) -- outputs "class 'aClass' (table: 0x0002cee8)"

:classOf()

This built-in function returns true when the class from which the method is called is a superclass of a given class.

local aClass = class("aClass")
local aSubClass= aClass:extend()
print(aClass:classOf(aSubClass)) -- outputs true

It also returns true even if the given class is not a direct subclass, as long as they are related.

local classLevelOne = class("classLevelOne")
local classLevelTwo = classLevelOne:extend("classLevelTwo")
local classLevelThree = classLevelTwo:extend("classLevelThree")

print(classLevelOne:classOf(classLevelTwo)) -- outputs true
print(classLevelOne:classOf(classLevelThree)) -- also outputs true

:subclassOf()

This built-in function returns true when the class from which the method is called is a subclass of a given class.

local aClass = class("aClass")
local aSubClass= aClass:extend()
print(aSubClass:subclassOf(aClass)) -- outputs true

It also returns true even if the given class is not a direct superclass, as long as they are related.

local classLevelOne = class("classLevelOne")
local classLevelTwo = classLevelOne:extend("classLevelTwo")
local classLevelThree = classLevelTwo:extend("classLevelThree")

print(classLevelThree:subclassOf(classLevelTwo)) -- outputs true
print(classLevelThree:subclassOf(classLevelOne)) -- also outputs true

:subclasses()

This is another built-in class function which returns the list of all subclasses from a given class. For example, let us consider the following hierarchy:

local GraphicalObject = class('GraphicalObject')
local Rect = GraphicalObject:extend('Rect')
local Window = Rect:extend('Window')
local Button = Rect:extend('Button')

The GraphicalObject class has three subclasses: Rect, Window and Button. The Rect class has two subclasses: Window and Button.

We can list them using :subclasses():

local subclasses = GraphicalObject:subclasses()
for _, subclass in ipairs(subclasses) do print(subclass) end -- outputs 'Rect', 'Window' and 'Button'.

local subclasses = Rect:subclasses()
for _, subclass in ipairs(subclasses) do print(subclass) end -- outputs 'Window' and 'Button'.

Eventually, :subclasses() can take an optional function argument filter, plus a vararg to be passed as arguments to the function filter. This filter should be implemented according to the following prototype:

local filter(subclass, ...)
  if someCondition then return true end
end

This filter function will be applied to all the subclasses of a given class. When this filter evaluates to false or nil, the subclass will not be included in the returned collection. For example, we can use this filter only the direct subclasses of GraphicalObject class.

local direct_subclasses = GraphicalObject:subclasses(function(s)
  return s.super == GraphicalObject
end)
for _, subclass in ipairs(direct_subclasses) do print(subclass) end -- outputs 'Rect'.

:instances()

This built-in class function returns the list of all instances from a given class. For example, let us consider the following hierarchy:

local GraphicalObject = class('GraphicalObject')
local Rect = GraphicalObject:extend('Rect')
local Window = Rect:extend('Window')
local Button = Rect:extend('Button')

local aRect = Rect()
local aWindow = Window()
local aButton = Button()

The Rect class has three instances: aRect , aWindow and aButton. The Window class has a single instance: aWindow The Button class has a single instance: aButton

This can be shown in Lua code:

local instances = Rect:instances()
for _, instance in ipairs(instances) do print(instance) end -- outputs `aRect `, `aWindow` and `aButton`.

local instances = Window:instances()
for _, instance in ipairs(instances) do print(instance) end -- outputs `aWindow`.

local instances = Button:instances()
for _, instance in ipairs(instances) do print(instance) end -- outputs `aButton`.

Similarly to :subclasses(), :instances() takes an optional filter function prototyped as follows:

local filter(instance, ...)
  if someCondition then return true end
end

This filter function will be applied to all the instances of a given class. When this filter evaluates to false or nil, the instance will not be included in the returned collection. For example, we can use this to filter only the direct instances of Rect class.

local direct_instances = Rect:instances(function(s)
  return s.class == Rect
end)
for _, instance in ipairs(direct_instances) do print(instance) end -- outputs 'aRect'.