Here’s a simple code of a class I made:
local class = {}
class.__index = class
function class.new()
local object = {}
setmetatable(object ,class)
return object
end
function class:Print()
print("Hey")
end
local object = class.new()
object:Print()
When I do setmetatable(object, class) I basically say: If you cant find it in object, try look in class.
My issue is that, this doesn’t work unless I specify: class.__index = class (at row 2).
Seems incredibly redundant to have to say: if you dont find it in class, try look in class.
Can somebody explain exactly what the difference between setmetatable and __index is? And why I have to do class.__index = class or else it wont find anything?
1 Like
Functions and methods that are called on the returned object are technically considered “indices,” meaning it will invoke the __index
metamethod. Whenever the method can’t be found in the object, it looks for it in the class. I.e. there is no object:Print()
, but there is class:Print()
which is next on the line.
And this is where self
comes in. It points to the class’s current object that the method is running for. You can think of self
as a way for a common metatable of many tables to point to one specific one. And that specific one is what the method would be called on externally.
Also, doing this:
class = {}
class.__index = class
--somewhere down the line
setmetatable(object, class)
Is the same as:
local class = {}
--somewhere down the line
setmetatable(object, {__index = class})
It’s just put inside of the class table because it’d be inefficient to have separate metatables for each object.
2 Likes
It’s because you’re using the class as the metatable, which is fine but you don’t have to do that. I usually do
local Class = {}
local ClassIndex = {}
local ClassMT = {__index = ClassIndex}
function Class.New(...)
local self = setmetatable({}, ClassMT)
self:Initialize(...)
return self
end
function ClassIndex:Initialize()
end
function ClassIndex:New()
end
return Class
I like it a bit better because class methods and properties aren’t inherited by instances. E.g. you can’t do
local instance = Class.New()
local instance2 = instance.New()
because ClassIndex
doesn’t have a New
method, only the class does.
BTW, your the examples above differ in one way: calling getmetatable
on an instance from the first example would return the class table for every instance, in the second example it would be a different table for each instance and none of them would be the class table.
I’m not sure I understand which one does what…
If there isnt a method in object, it will look in class instead; because I used setmetatable, right?
Why doesn’t it find it right away then?
What is it that __index does that makes everything work?
@ThanksRoBama thanks for the input, but it seems excessivly confusing given the requirements I have for objects (basically just inheritance and data storage).
I made this image to explain how metatable.__index = metatable
works a while ago to explain to another guy since I’m not that good when it comes explaining it entirely in words so I hope this helps understanding it better:
1 Like
I’ve finally understood this concept!
If a key does not exist in table A, then it will check if table A has a metatable.
If table A has a metatable it will not check if the key exists in the metatable!
Rather it will check if the metatable has an __index function. If so, run the __index function with the key.
As you know, __index can be either a function or a table, so setting
metatable.__index = metatable
will ensure that, when it fails to find key in table A, it will use the __index of metatable to find the value. Since the __index points to a table, it will look in that table for the value.
And we had set that table to metatable, so that’s where it will look.
And that is the story of setmetatable and __index…
TL;DR
Basically, setting a metatable means adding a table of functions that will be used if the functions of the original table fail to execute. It does not mean adding default variables. If you want that, you set
metatable.__index = metatable
4 Likes