Skip to content
SuperUserNameMan edited this page Mar 16, 2021 · 5 revisions

Creating instances

An instance can be created from any class using class:new():

local appWindow = Window:new()

30log provides a syntactic sugar for creating instances, for convenience.

local appWindow = Window() -- same as Window:new()
print(appWindow.width, appWindow.height) -- outputs 150, 100

An instance has a free access to its class attributes.

print(appWindow.width, appWindow.height) -- outputs 150, 100

An instance also has its own copy of those attributes.
Assigning them new values will only affect these copies.

appWindow.width, appWindow.height = 720, 480
print(appWindow.width, appWindow.height) -- outputs 720, 480
print(Window.width, Window.height) -- outputs 150, 100, hence class attributes are left

Class constructors

From the previous examples, one might have noticed that once an instance is created, it already shares the attributes of his class.

Yet, instances can be initialized in a custom manner. In this way, they will have custom attributes and values already set. To achieve this, a class constructor should be implemented. Typically, this is a method (a function) that will be called internally within class:new().
30log uses a reserved key, named init for class constructors.

local Window = class("Window")
function Window:init(width, height)
  self.width, self.height = width, height
end

local appWindow = Window(800,600)
print(appWindow.width, appWindow.height) -- output 800, 600

The class constructor init can also be defined as a table with named keys, instead of a function. In that case, any new instance created will get a raw copy of the keys and values found in this table: this is called static instantiation.

local Window = class("Window")
Window.init = {width = 500, height = 500}

appWindow = Window()
print(appFrame.width, appFrame.height) -- outputs 500, 500

It is also possible to allocate a new instance via class:create(). In this case though, the class constructor, even if defined, will not be invoked.

local appWindow = Window:create()
print(appWindow:instanceOf(Window)) -- outputs "true"
print(appWindow.width, appWindow.height) -- outputs nil, nil

Window.width = 250 -- Sets a default width for the class
print(appWindow.width) -- outputs 250

instanceOf()

Instances from any class have a built-in method, named :instanceOf() to inspect if a given instance originates from a specific class. For example, we want to know if appWindow is an instance from Window class.

print(appWindow:instanceOf(Window)) -- outputs true

It also returns true even if the given class is not the direct class from which the instance originates, but rather, a superclass, as shown in the following example:

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

print(appWindow:instanceOf(Window)) -- outputs true
print(appWindow:instanceOf(Rect)) -- outputs true
print(appWindow:instanceOf(GraphicalObject)) -- outputs true

cast()

The class from which an instance originates can be changed via the cast() method.

local Rect = class('Rect')
local Window = class('Window')
local w = Rect()
print(w:instanceOf(Rect)) -- outputs true

w:cast(Window) -- Sets class 'Window' to be the class of instance 'w'
print(w:instanceOf(Rect)) -- outputs false
print(w:instanceOf(Window)) -- outputs true
print(w.class) -- outputs class 'Window'

When a cast was made, the instance has no longer access to its previous class methods, but instead it uses its new class methods.

local Rect = class('Rect')
function Rect:init(width, height)
  self.width, self.height = width, height
end

function Rect:area()
  return self.width * self.height
end

local Window = class('Window')
function Window:init(width, height)
  self.width, self.height = width, height
end

function Window:halve()
  self.width, self.height = width / 2, height / 2
end

local w = Rect(10, 5)
print(w:area()) -- outputs 50

w:cast(Window)
w:area() -- raises an error
w:halve()
print(w.width, w.height) -- outputs 5, 2.5

Debugging instances

As for classes, passing an instance to print or tostring returns a string representing the instance. This string specifies the passed-in object is an instance, gives its class name and the memory address of the instance.

local Window = class("Window")
local appWindow = Window()
print(appWindow) -- "instance of 'Window' (table: 0x0002cf70)"

local Window = class()
local appWindow = Window()
print(appWindow) -- "instance of '?' (table: 0x0002cf70)"

Also, instances have an attribute named class which points to their class.

print(appWindow.class) -- "class 'Window' (table: 0x0002cdf8)"

30log classes are metatables of their own instances. This implies that one can inspect the relationship between a class and its instances using getmetatable.

local aClass = class()
local someInstance = aClass()
print(getmetatable(someInstance) == aClass) -- true