Possible Implementation of Classes in Lua - noooway/love2d_arkanoid_tutorial GitHub Wiki
From a technical point, a class in Lua is a table, containing class functions (a.k.a. methods) and class variables.
Each instance (a.k.a. object) of the class is also a table, configured such that any missing field is looked at the class-table. This allows objects to have customized properties stored in their own table, while sharing common properties using the class table. In Lua, such arrangement is achieved by configuring so-called __index
metamethod in each instance of the class. This __index
metametod is either a function or another table that is used when an inquired field is missing in the quered table.
Here is an example:
> class_A = {}
> class_A.name = 'The Class'
> class_A.say_hello_method = function( z ) print( "hello from " .. z.name ) end
> class_A.name
The Class --(*1)
> class_A.say_hello_method( class_A )
hello from The class --(*2)
> object_of_class_A = {}
> object_of_class_A.name
nil --(*3)
> object_of_class_A.__index = class_A
> setmetatable( object_of_class_A, object_of_class_A )
> object_of_class_A.name
The Class --(*4)
> object_of_class_A.say_hello_method( object_of_class_A )
hello from The Class
> object_of_class_A.name = "An object"
> object_of_class_A.say_hello_method( object_of_class_A )
hello from An Object --(*5)
(*1): Field name
in the class_A
table is set to "The Class".
(*2): We call a function, stored in the field say_hello_method
of the class_A
table.
It expects a single argument z
. We pass class_A
itself as such argument.
(*3): We define a table object_of_class_A
. An attempt to index it's name
field returns nil
.
(*4): After __index
and setmetatable
magic, an attempt to index object_of_class_A.name
field doesn't return nil
any longer. However, at this point the field name
still isn't set at the object_of_class_A
, so the attempt to index it results in an invocation of the __index
method. In this case, it points to the class_A
, where it redirects the query for the name
. The value of this field is 'The Class', which is printed in the output.
(*5): We set field name
in the object_of_class_A
. Now it can be successfully indexed without any redirections. Still, the field say_hello_method
does not present in the object_of_class_A
and it's value is searched for in the class_A
(where __index
points to).
There is a syntactic trick in Lua, which simplifies writing and calling methods — the colon syntax. When a method is prefixed by the colon instead of the dot, a table before ':' is substituted as the first formal parameter of the method:
some_table:function_inside_table()
-- is equivalent to
some_table.function_inside_table( some_table )
That way it is possible to write:
> class_A:say_hello_method()
hello from The Class
> object_of_class_A:say_hello_method()
hello from An Object
It is also possible to define methods using colon syntax.
In that case, a first formal parameter with name self
is implied.
function some_table:function_inside_table( arg1, arg2, etc )
.....
end
-- is equivalent to
function some_table.function_inside_table( self, arg1, arg2, etc )
.....
end
In our case:
> function class_A:self_say_hello_method( hello_ending )
>> print( "self-hello from " .. self.name .. hello_ending )
>> end
> class_A:self_say_hello_method( "!!!" )
self-hello from The Class!!!
> object_of_class_A:self_say_hello_method( "???" )
self-hello from An Object???
The dot and the colon forms can be mixed freely:
> object_of_class_A.self_say_hello_method( object_of_class_A, "!!!" )
self-hello from An Object!!!
A common Lua idiom for class constructor looks like this:
function Class:new( o )
o = o or {}
setmetatable(o, self)
self.__index = self
o.name = o.name or "Class"
o.property_1 = o.property_1 or 10
o.property_2 = o.property_2 or 20
.....
return o
end
setmetatable(o, self)
sets Class as the metatable for an each new Class
object.
self.__index = self
sets __index
metamethod in the Class
to point to Class
itself.
To implement inheritance, it is necessary to configure __index
metamethod of the child class to point to the table representing the parent class. In this case it can be done simply by SubClass = Class:new()
. Then it is possible to proceed defining SubClass
methods. I won't need this, since I'm not going to use inheritance in this project.
One last thing: in Lua syntax some_function{}
is identical to some_function( {} )
.
Such shorthand is commonly found in calls to class constructors, so don't be surprised.
.....
It is possible to pack all these low-level details into a set of functions that can be used to define classes.