Skip to content
CKortylak edited this page Aug 29, 2020 · 6 revisions

middleclass 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')

Getting the name of the class: name

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"

Getting the superclass of a class: super

local MySubclass = class('MySubclass', MyClass)
local superclass = MySubclass.super

Returns the superclass of the class. If a class has no superclass it returns nil.

Defining static methods: static

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.

Creating subclasses: subclass, subclassed & isSubclassOf

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

Creating new instances: new, initialize, allocate & isInstanceOf

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

Instance metamethods

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.

Modifying the class: :include(Mixin), static methods & included

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.