-
Notifications
You must be signed in to change notification settings - Fork 191
Reference
middleclass provides some default methods for its classes and instances. Here's a list of those methods.
All the examples below assume that middleclass is loaded in a variable in a way similar to this:
local class = require 'middleclass'
Let's assume that MyClass
is a class defined like so:
local MyClass = class('MyClass')
local name = MyClass.name
Returns the name of the class, as a string ('MyClass'
, in this particular case).
middleclass modifies the classes' metatables in several ways. One of them changes the __tostring
metamethod, so the class name is used when transforming it into a string:
print(MyClass) -- prints "class MyClass"
local MySubclass = class('MySubclass', MyClass)
local superclass = MySubclass.super
Returns the superclass of the class. If a class has no superclass it returns nil
.
function MyClass.static:bar()
print('this is a static method')
end
...
MyClass:bar() -- prints 'this is a static method'
Static methods (also known as "class methods") are methods which apply to the class itself, not its instances. They must be defined using the static
namespace.
If you see self
in the body of a static method, take into account that self
will point the class itself, not an instance of the class, like it happens in non-static methods.
local MySubclass = MyClass:subclass('MySubclass')
In addition to using the class('MySubclass', MyClass)
syntax, you can also use the :subclass
method. If your class needs to make something "special" when being subclassed, you could override this static method to add extra functionality here. The class('MySubclass', MyClass)
syntax internally calls MyClass:subclass
.
When a subclass is created, the static method subclassed
is called on its parent. By default, this method does nothing. But you could override it to do something when a particular class is subclassed:
function MyClass.static:subclassed(other)
print("Created a subclass of MyClass: " .. other.name)
end
-- prints "Created a subclass of MyClass: MySubclass
local MySubclass = MyClass:subclass('MySubclass')
You can check if a class is a subclass of another class by using the isSubclassOf
static method:
local MySubclass = MyClass:subclass('MySubclass')
local Other = class('Other')
print(MySubclass:isSubclassOf(MyClass)) -- true
print(Other:isSubclassOf(MyClass)) -- false
isSubclassOf
traverses the whole hierarchy of classes (if A
is a superclass of B
, and B
is a superclass of C
, then C:isSublclassOf(A)
returns true
). If you want to check that a class is exactly the parent of another class, use super
:
print(MySubclass.super == MyClass) -- true
local obj = MyClass:new()
The ultimate purpose of a class is creating instances (also called objects). This is done with new
. new
is a static method which exists in all classes. It has a default implementation which creates almost-empty instances.
As an alternative syntax, you can instantiate members of a class with a short-cut syntax, using the class as if it was a function, skipping the :new
:
local obj = MyClass()
Both options (using new
or using the shorter version) are equivalent and produce the same result (the short version is implemented in terms of new
, so it is marginally slower).
You can modify new
's behavior by defining an instance method called initialize
:
function MyClass:initialize(x,y)
self.x = x
self.y = y
end
local obj2 = MyClass:new(10, 20)
print(obj2.x, obj2.y) -- 10, 20
If you are modifying the constructor of a subclass, you can call the superclass' constructor with this syntax: Superclass.initialize(self, <params>)
local Subclass = class('Subclass', MyClass)
function Subclass:initialize(x,y,z)
MyClass.initialize(self, x, y)
self.z = z
end
local obj3 = Subclass:new(10,20,30)
print(obj3.x, obj3.y, obj3.z) -- 10, 20, 30
Middleclass also provides a low-level instance method called allocate
. This is what new
calls to create an instance (a table with some metatable properties set up), before calling initialize
on it. You may want to modify the allocate
method if you need to alter the way instances are created, before calling initialize
. This is mostly useful if you are making profound modifications in the way instances are created; for regular uses, you don't need it.
Finally, isInstanceOf
is an instance method which can be used to check if an instance is of a certain class:
local obj = MyClass:new()
print(obj:isInstanceOf(MyClass)) -- true
print(obj:isInstanceOf(Other)) -- false
isInstanceOf
traverses the whole class hierarchy (if A
is a superclass of B
, B
is a superclass of C
, and c
is an instance of C
, then c:isInstanceOf(A)
returns true
). If you want to make sure that the class is exactly the same, you must use the .class
attribute:
print(obj.class == MyClass) -- true
By default, all instances come with a default __tostring
metamethod which generates the following string when an instance is printed out:
print(obj) -- prints "Instance of class MyClass"
You can change this by redefining the __tostring
method in the class:
function MyClass:__tostring()
return "This is a funky instance of " .. self.class
end
local obj2 = MyClass:new()
print(obj2) -- prints "This is a funky instance of class MyClass"
All other metamethods, including __index
are supported, but only __tostring
is defined by default. See the metamethods doc for more information about them.
local SomeMixin = { foo = function() print('foo') end }
local myclass = MyClass:include(SomeMixin)
You can include mixins in a class with include
. Notice that include
returns the class. This means that if you already know the mixin you want to include in the class when you are creating it, you can do so in one line:
local MyOtherClass = class('MyOtherClass'):include(SomeMixin)
Mixins can have two special properties: if they have a subtable called static
, the methods defined there are static instead of instance-based.
If a mixin has a property called included
, that method is not added to the class; instead, it is called right when the inclusion occurs, passing the class as parameter. This allows creating mixins which modify the classes which include them in more profound ways than just adding new methods. This can be very powerful. In the included
method of a mixin, you can:
- Change the way instances are created by redefining the
allocate
method - Change the way the subclasses are created by modifying the static
subclass
method - Make more profound changes in the way instance method lookup works, by modifying the instance indexes
You can find examples of all this in stateful.lua.
Also, see the mixins doc for more information about them.