Inherit metatet (class) and use its required constructor parameter
I found this tutorial: http://lua-users.org/wiki/InheritanceTutorial
I have a metatet called Creature. The entity requires an argument in its constructor. The required argument is a string that represents the name.
local creature = Creature(name)
- The creature has many other methods, for example
getDescription()
. - The creature
getDescription ()
returns the string: "This creature." - The creature
getName ()
returns the string: name
I want to create a new metatable (class) called Player
and inherit from its Creature
metatable (class)
- The Player class will only override the getDescription () method.
- The Player class also inherits the creature's getName () method.
- The getDescription () player returns the string: "This is the player".
I want to be able to do the following:
local creature = Creature("Bob")
print(creature:getDescription())
print(creature:getName())
local player = Player("Joey")
print(player:getDescription())
print(player:getName())
Should print:
This creature
Bob
This is
Joey's player
Basically my problem is that the entity class requires an argument to identify someone, a name. Its function getName ()
takes the value in the argument and prints it. If I'm going to use Player to inherit all of the Entity's functions (and override if necessary), how do I change the code to make sure the Player receives the argument it needs?
Code taken from the tutorial:
-- 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 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
source to share
I want to be able to do the following:
local creature = Creature("Bob") print(creature:getDescription()) print(creature:getName()) -- ...
Supporting this kind of syntax for using a class is certainly possible in lua - it's just a matter of how to use language mechanisms and tools to achieve this. Some important questions to be addressed:
- How will the construction project be built? What function should the call of the class call to initialize the instance?
- What will the instance object look like from the client code?
- How is method overriding done?
From the tutorial, the wiki new_class:create()
creates a new instance, but doesn't call any build function. Thus, in your OOP system, you will need to select a constructor-like function that the client code will provide and will trigger the creation of the class. For example:
function new_class:create(...)
local instance = setmetatable( {}, class_mt )
if new_class.__init__ then new_class.__init__(instance, ...) end
return instance
end
Here I am just using __init__
as a constructor name, similar to python, but in fact any name will work as long as the code and create code match.
To maintain the syntax of creating an object, for example Creature("Bob")
, Player("Joey")
you can either make their actual function calls or use a metathet __call
. Using the latter is a simple assignment __call
:
function inheritsFrom( baseClass )
local new_class = setmetatable( {}, { __index = baseClass } )
-- ...
getmetatable(new_class).__call = new_class.create
return new_class
end
In the last question, you are overriding an existing method by simply assigning a new function to it in a derived class. eg. So, to override getDescription
in Player
, you can do:
function Player:getDescription()
return "Is a player"
end
Putting it all together inheritsFrom
function inheritsFrom( baseClass )
local new_class = setmetatable( {}, { __index = baseClass } )
local class_mt = { __index = new_class }
function new_class:create(...)
local instance = setmetatable( {}, class_mt )
if new_class.__init__ then new_class.__init__(instance, ...) end
return instance
end
getmetatable(new_class).__call = new_class.create
return new_class
end
Class definition Creature
+ one instance of a creature:
local Creature = inheritsFrom
{
__init__ = function(self, name) self.name = name end;
getDescription = function(self) return "Is a creature" end;
getName = function(self) return self.name end;
}
local bob = Creature "Bob"
print(bob:getDescription())
print(bob:getName())
Subclassing Player
from Creature
and overriding getDescription
:
local Player = inheritsFrom(Creature)
function Player:getDescription()
return "Is a player"
end
local joey = Player "Joey"
print(joey:getDescription())
print(joey:getName())
As a final note, the lua Penlight library already implements a class system similar to the one I described above, but much more functional and complete. If this is not an exercise, consider using this instead of reinventing another lua OOP system.
source to share