So I know Java pretty well, but I am mostly self taught in Lua. Is self in Java similar to self in Lua?
Javaās this
which can be used to reference the object in the constructor, is like self
in Lua.
In java, something similar to self is this. You can find more informations on google. Then, self and this are not really that same. With this you can call the constructor of a class, what self not can. But the reason why i say that both are similar is this:
local Class = {}
Class.__index = Class
function Class:new(...) --Class Constructor
local NewClass = {}
setmetatable(NewClass, Class)
NewClass.Variadic = ...
return NewClass
end
--You canāt do: self(...) as with this(...), but you can do self.new(...)
function Class:printVariadic()
print(self.Variadic) --Its works
end
And this would be in java:
public class Class {
private string StringTest;
public Class(StringTest) {
this.StringTest = StringTest;
}
Public GetStringTest() {
return this.StringTest
}
}
Sorry, i canāt really explain this, you should try yourself. I forgot how java works and idk if 100% what i say is correct, so if you find any problem then write it me! Hope this helps!
I do not think metatables are necessary to explain self
.
That is true for local variables, but not for values. ...
is not a tuple of variables, it is a tuple of values. You can still put it in a table and iterate over it even with over 200 elements.
self
can be easily explained just by showing its only automatic use case:
local myTable = {}
function myTable:myMethod()
-- now `self` exists which is only 1 thing: the first argument of the function call
print(self)
end
-- when using a method-like call to a function, the value you are calling it from becomes the first argument
myTable:myMethod(true) -- now `self` becomes `myTable` and so it prints `table: 123456789`
myTable.myMethod(true) -- if you do not use a colon (:), it acts like a normal function and so `self` becomes the first argument which is `true` and it prints `true`
myTable.myMethod(myTable, true) -- this is equivalent to `myTable:myMethod(true)
self
is automatically created only when you define a method-like function using a colon (:
), but you can completely copy the behavior into a normally defined function just by putting self
as the first parameter, and it can still be used exactly like a method with a colon (:
) if you put it in a table since the way you choose to call the function is the deciding factor to what self
becomes.
See:
My statement still stands.
Looks like functions can have a max of 50? Either way there is no reason to have hit this limit.
Thanks, I was unsure exactly but this helped me understand it a lot better. Thanks to you too @XxELECTROFUSIONxX
Fun fact: donāt make a function like so:
function module:f()
print(self:f())
end
It will crash your gameā¦
Itās like:
while true do
print(1)
end
This repeats the same and the same
In your function, you are calling the function, wich calls the function self again, and again, and again. I hope that no ones did itā¦
Yes.
Itās called a Stack overflow!
So itās called stack overflow
also known as a stack overflow
Hereās an example of self being used in my accounts module:
-- global table
local account = {
names = {}
};
-- functions
function account:get(name, password)
if self.names[name] and password == self.names[name][2] then
return self.names[name][3] elseif self.names[name] and not password == self.names[name][2] then
return nil
else
return nil
end
end
function account:set(truePassword, name, password, data)
if self.names[name] and truePassword == self.names[name][2] then
self.names[name] = {name, password, data}
else
print('set failed')
end
end
function account:setraw(name, password, data)
self.names[name] = {name, password, data}
end
function account:getraw(name)
if self.names[name] then
return self.names[name][3]
else
return nil
end
end
function account:clear(name, password)
if self.names[name] and password == self.names[name][2] then
self.names[name] = nil
else
print('clear failed')
end
end
function account:clearraw(name)
if self.names[name] then
self.names[name] = nil
else
print('clear failed')
end
end
return account
Basically, self is just the table being returned. Of course, since iām only using methods (tbl:function instead of tbl.function), I donāt need to define self in the first place.
I still donāt get why in some lua OOP you do
local t = {}
t.__index = t
like this one.
Iāve been trying to figure it out but my brain keeps having a meltdown. Yes I know that if t.x is nil then t.__index comes next which is t, but thatās completely pointless because itās just going over the same table. I thought it was used because of self I guess it wasnāt.
Yea I donāt understand that too. Iām guessing itās just creating the new index metatable. So it detects if the item being added to the table isnāt already there, then if itās not, add it in.
Because table.insert could insert the same item twice instead of replacing it, Im assuming.
This is not pointless when youāre using it as a constructor (there are many other optimal ways to use __index
, but considering all of the examples above, I will dissect precisely what is happening in their typical examples, then showcase other examples where the __index
metamethod will be used.
Consider this script for instance:
local SomeModule = {}
SomeModule.__index = SomeModule
function SomeModule.new()
local self = {}
self.ThingToPrint = ""
return setmetatable(self, SomeModule)
end
function SomeModule:SetThingToPrint(NewThingToPrint)
self.ThingToPrint = NewThingToPrint
end
function SomeModule:GetThingToPrint()
return self.ThingToPrint
end
function SomeModule:PrintThing()
print(self:GetThingToPrint())
end
local PrinterOne = SomeModule.new()
local PrinterTwo = SomeModule.new()
local PrinterThree = SomeModule.new()
PrinterOne:SetThingToPrint("Hello!")
PrinterTwo:SetThingToPrint("Goodbye!")
PrinterThree:SetThingToPrint("Some greeting!")
PrinterOne:PrintThing()
PrinterTwo:PrintThing()
PrinterThree:PrintThing()
If you run this code, this will print the following:
Behind the scenes, this is what is happening:
I will use PrinterOne
as an example, though PrinterTwo
and PrinterThree
will undergo the same exact processes.
Letās start with
local PrinterOne = SomeModule.new()
SomeModule
is a reference to the actual SomeModule
table, which would look something like this:
(I have cleared out the functions in this case to make it more clear to read):
local SomeModule = {
["new"] = (function() end)
["__index"] = {
["new"] = (function() end)
["__index"] = . . . (SomeModule)
["SetThingToPrint"] = (function() end)
["GetThingToPrint"] = (function() end)
["PrintThing"] = (function() end)
}
["SetThingToPrint"] = (function() end)
["GetThingToPrint"] = (function() end)
["PrintThing"] = (function() end)
}
Notice that __index
references itself, therefore the table inside of __index
will always be itself continously forever, hence why it is represented with . . .
Okay. So weāve called SomeModule.new()
and set its return value to equal PrinterOne
.
As we call SomeModule.new()
, internally, the execution instruction set goes through line by line of SomeModule.new()
It first creates the self
table, which is empty and not populated at the time.
Then, it populates ThingToPrint
inside of self
as an empty string value.
Lastly, it returns the metatable of self
to SomeModule
. In order to do so, the instruction set needs to index SomeModule. It first checks whether the __index
metamethod is valid. Because our __index
metamethod is, in fact, valid, it points to SomeModule["__index"]
instead of directly SomeModule
.
This will allow us to get copy the self
value that was created in the module over to PrinterOne
, and us being able to independently change self
for every time we call SomeModule.new()
without having the values associated in self
being overwritten.
It would be bad if we wanted to do this without an __index
metamethod:
local PrinterOne = SomeModule.new()
local PrinterTwo = SomeModule.new()
as later in our code, we wonāt be able to seperate our changes from PrinterOne
or PrinterTwo
.
Consider this example without __index
, for instance:
local SomeModule = {}
function SomeModule.new()
local self = {}
self.ThingToPrint = ""
return self
end
function SomeModule:SetThingToPrint(NewThingToPrint)
self.ThingToPrint = NewThingToPrint
end
function SomeModule:GetThingToPrint()
return self.ThingToPrint
end
function SomeModule:PrintThing()
print(self:GetThingToPrint())
end
local PrinterOne = SomeModule.new()
local PrinterTwo = SomeModule.new()
local PrinterThree = SomeModule.new()
PrinterOne:SetThingToPrint("Hello!")
PrinterTwo:SetThingToPrint("Goodbye!")
PrinterThree:SetThingToPrint("Some greeting!")
PrinterOne:PrintThing()
PrinterTwo:PrintThing()
PrinterThree:PrintThing()
The defintions for PrinterOne, PrinterTwo, and PrinterThree would run fine but the moment it hits the methods, it will error.
This is because we no longer have access to the methods of SomeModule
, only whatever is inside of self
, which is ThingToPrint
.
With or without __index
, you will always end up having this:
local PrinterOne = {
["ThingToPrint"] = ""
}
(Remember the instructions that are being made are roughly this):
local PrinterOne
PrinterOne = SomeModule.new() = self = {
["ThingToPrint"]
}
__index
allows PrinterOne
to access and call methods from SomeModule
without filling its table. You can now probably see why its problematic if you leave out the __index
.
After called SomeModule.new()
and setting it to the value of PrinterOne
, we then call PrinterOne:SetThingToPrint("Hello!")
By this point, we have already defined PrinterOne, PrinterTwo, and PrinterThree, and SomeModule as follows: (note that the memory address of SomeModule
is exactly the same as all __index
's present inside of SomeModule
, as it is a reference of itself.)
Continuing on, "Hello!"
gets passed through as NewThingToPrint = "Hello!"
and self.ThingToPrint = NewThingToPrint
(Note that the memory address of self
does not point to SomeModule
, but points to PrinterOne
.)
This calls the function PrintThing
from SomeModule
. PrinterOne
is a table, similar to SomeModule
, However, PrinterOne
does not actually contain any functions inside of it. PrinterOne
is a table consist of only ThingToPrint
inside of it.
Then it continues doing the same operations for the other method calls:
Then finally, the PrintThing()
method is called (which just prints self.ThingToPrint
)
Let me know if you have any questions on this, Iāll be happy to break it down even further.
Oh I see! So the __index metamethod used in this way is basically a gateway that allows you to run functions/methods that doesnāt exist in self from a separate table. If we were to just do a for loop to copy all the methods and return it it would mean that we could potentially overwrite the methods and break it. And because itās returning a reference point to the methods, all the other objects that were created before it could have its own methods overwritten.
In short term, every object created under a class can access that classās special methods without writing to it.
I see the importance of it now, and Iām really thankful that you explained it thoroughly. I do have one question though, is that thing you used to display the table a Call Stack?
You can also do this
local mod = {}
mod.__index = mod
function mod.new(Player)
local self = {} -- create a dictionary for self
self.Backpack = Player.Backpack
return setmetatable(self, mod) -- return self in a metatable
end
function mod:AddToolToInventory(Item) -- just a little test function
Item.Parent = self.Backpack
end
return mod
-----------------Server
local UT = require(MODULE_PATH)
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(Player)
local _Utilis = UT.new(Player)
local example = toolpath:Clone()
_Utilis:AddToolToInventory(example)
end)```
A question
If there are more than 1 aruguements passed to this, self will take which arugmenet?
For what Iāve seen is that self takes the first argument no matter what, so if you were to do someObject.Method(self) and pass āamogusā as self, then when youāre referencing self itād be the string āamogusā
i.e print(self) ā āamogusā
though you can test this on your own.