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.