Inheritance Tutorial |
|
The following example implements a class with no inheritance:
SimpleClass = {}
SimpleClass_mt = { __index = SimpleClass }
-- This function creates a new instance of SimpleClass
--
function SimpleClass:create()
local new_inst = {} -- the new instance
setmetatable( new_inst, SimpleClass_mt ) -- all instances share the same metatable
return new_inst
end
-- Here are some functions (methods) for SimpleClass:
function SimpleClass:className()
print( "SimpleClass" )
end
function SimpleClass:doSomething()
print( "Doing something" )
end
In the above example, SimpleClass represents a table that holds all of our class's methods, like a class declaration. SimpleClass_mt is the metatable we will attach to each class instance we create. The function SimpleClass:create() creates an instance of our class SimpleClass. Construction of a class instance involves creating an empty table and then attaching our SimpleClass metamethods to it. The result of attaching the metamethods is that the new instance looks to the metatable we attached for its customised behaviour.
Method invocations on the instance will trigger the "index" event on the instance, causing a lookup on the "__index" member of the instance's metatable. The __index member is simply a reference to SimpleClass. Therefore, method invocations on the instance will cause a lookup in the SimpleClass table.
Here is an example:
> simple = SimpleClass:create() > > simple:className() SimpleClass > > simple:doSomething() Doing something
Now we want to create a new class SubClass that inherits and, optionally, overrides functions from SimpleClass.
-- Create a new class that inherits from a base class -- function inheritsFrom( baseClass ) -- The following lines are equivalent to the SimpleClass example: -- Create the table and metatable representing the class. local new_class = {} local class_mt = { __index = new_class } -- Note that this function uses class_mt as an upvalue, so every instance -- of the class will share the same metatable. -- function new_class:create() local newinst = {} setmetatable( newinst, class_mt ) return newinst end -- The following is the key to implementing inheritance: -- The __index member of the new class's metatable references the -- base class. This implies that all methods of the base class will -- be exposed to the sub-class, and that the sub-class can override -- any of these methods. -- if baseClass then setmetatable( new_class, { __index = baseClass } ) end return new_class end
The function inheritsFrom(baseClass) takes a single argument, the class declaration we want to inherit from. The function returns a class declaration which we can then tailor. new_class is the new class declaration to be returned. The nested function new_class:create() is part of the class declaration returned and will create new instances of the sub class we are creating. This function creates a newinst table which uses our new class table to hold it's methods. The new class table in turn looks in the baseClass if it cannot find a method we require, and thus we inherit it's methods.
Building on SimpleClass we now create a class called SubClass that inherits from SimpleClass and overrides className():
> -- Create a new class that inherits from SimpleClass > SubClass = inheritsFrom( SimpleClass ) > > -- override className() function > function SubClass:className() print( "SubClass" ) end > > -- Create an instance of SimpleClass > simple = SimpleClass:create() > > simple:className() SimpleClass > > simple:doSomething() Doing something > > -- Create an instance of SubClass > sub = SubClass:create() > > sub:className() -- Call overridden method SubClass > > sub:doSomething() -- Call base class method Doing something >
We can now expand on our inheritance structure and add features that are common in other languages, like access to a class's super class and a isa() method that provides type id functionality:
-- A new inheritsFrom() function -- function inheritsFrom( baseClass ) local new_class = {} local class_mt = { __index = new_class } function new_class:create() local newinst = {} setmetatable( newinst, class_mt ) return newinst end if nil ~= baseClass then setmetatable( new_class, { __index = baseClass } ) end -- Implementation of additional OO properties starts here -- -- Return the class object of the instance function new_class:class() return new_class end -- Return the super class object of the instance function new_class:superClass() return baseClass end -- Return true if the caller is an instance of theClass function new_class:isa( theClass ) local b_isa = false local cur_class = new_class while ( nil ~= cur_class ) and ( false == b_isa ) do if cur_class == theClass then b_isa = true else cur_class = cur_class:superClass() end end return b_isa end return new_class end
And, an example of usage:
> SimpleClass = inheritsFrom( nil ) -- pass nil because SimpleClass has no super class > > SubClass = inheritsFrom( SimpleClass ) > > FinalClass = inheritsFrom( SubClass ) > > sub = SubClass:create() > fc = FinalClass:create() > > print( fc:isa( SubClass ) ) true > print( fc:isa( FinalClass ) ) true > print( sub:isa( SubClass ) ) true > print( sub:isa( FinalClass ) ) false