Quick Links: Download Gideros Studio | Gideros Documentation | Gideros community chat | DONATE
is it possible to call subclass's overwrited function in baseclass's init function — Gideros Forum

is it possible to call subclass's overwrited function in baseclass's init function

alexzhengalexzheng Guru
edited January 2013 in General questions
Base = Core.class()
 
function  Base:init()
	self:say('balabala')
end
 
function Base:hello()
	print('hello from base')
 
end
 
function Base:say(what)
	print('say from Base', what)
end
 
 
Child = Core.class(Base)
 
function  Child:init()
	--self:say('balabala')
end
 
function Child:hello()
	print('hello from Child')
end
 
function Child:say(what)
	print('say from Child', what)
end
 
local child = Child.new()
 
child:hello()
now the output is
say from Base balabala
hello from Child

it will be better if output is
say from Childbalabala
hello from Child

Likes: Yan

+1 -1 (+1 / -0 )Share on Facebook
«1

Comments

  • atilimatilim Maintainer
    edited January 2013 Accepted Answer
    But in base class' init function, derived class hasn't been initialized/constructed yet. Therefore, it correctly calls Base:say. I think OOP languages also work in this way.
  • Oh,yes
    I tried it in C++, Base Class always call it's own method in construct.
    May be I must move all of my overwritable functions out of the init call.

    :))
  • bowerandybowerandy Guru
    edited January 2013 Accepted Answer
    This problem has bugged me several times.
    But in base class' init function, derived class hasn't been initialized/constructed yet. Therefore, it correctly calls Base:say. I think OOP languages also work in this way.
    Hmm.. there's a discussion about this here:

    http://stackoverflow.com/questions/36832/virtual-functions-in-constructors-why-do-languages-differ

    All I can say is that in Smalltalk (which in my opinion is the original and "correct" OO language) it *is* possible to call overridden functions in an initialize (constructor) method. The problem with the current way of working is that it often makes it difficult to specialise the way an object is created without having to create an intermediate abstract base class or a "wrapper" factory method.

    This is specifically important when adding event listeners. Let's say that I have a base class with an ENTER_FRAME listener and I want to override this in a subclass with another different ENTER_FRAME listener.
    Base=Core.class(Sprite)
     
    function Base:onEnterFrame()
        print("In base enterframe")
    end
     
    function Base:setUpListeners()
         self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
    end
     
    function Base:init()
        self:setUpListeners()
    end
     
    Derived=Core.class(Base)
     
    function Derived:onEnterFrame()
        print("In derived enterframe")
    end
     
    function Derived:setUpListeners()
         self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
    end
     
    function Derived:init()
    end
     
    stage:addChild(Derived.new())
    So what happens here is only the base listener is set up, which is rather unhelpful.

    If I wanted the original base listener functionality I would expect to call the super class method like this:
    function Derived:onEnterFrame()
        Base.onEnterFrame(self)
        print("In derived enterframe")
    end
    FWIW, Middleclass is a neat Lua class system that is very similar to the Gideros class framework but that appears to handle the above in the way I'd expect. It also has some nice features like Class.super and Class.name and mixins (but I wouldn't use the latter myself)

    best regards
  • atilimatilim Maintainer
    edited January 2013 Accepted Answer
    @bowerandy - I understand exactly. The current system was designed more like C++ than other OO languages. (In C++, calling a virtual function in constructor will never go to a more derived class than that of the currently executing constructor). After reading the discussion on stackoverflow, I understand that this is not the case with other OO languages.

    Currently, if Base class is registering an ENTER_FRAME event (even through another function), you cannot change/alter/override it with a Derived class.

    Let me play with Middleclass some more. I know you're a master of Smalltalk and thank you for great information.
  • atilimatilim Maintainer
    edited January 2013
    Also I can change this behavior. I just need to postpone calling of init functions so that they will be called after all the construction is finished. But I currently cannot decide whether I should change it or not.

  • @atilim, much as I would like to see this behaviour, I suspect you shouldn't change it as it will be a "breaking change" and will cause existing programs not to work.

    One thing you could add, though, is an init2() function (please choose a better name) that is automatically called by the framework on an object after it has been created. That way, people who want to use the functionality described above can ignore init() and just implement init2() or they can use use a combination of both.

    One of the things that makes the Gideros class system generally easier to use is the fact that the superclass constructor is automatically called for you. However, this can also be inflexible since, sometimes, you'd really like to be able to choose when it is called. You could use the init2() idea to implement a more traditional constructor scheme where one has to explicitly decide when/if to call the super constructor (i.e. when using init2() the super ctor is not called automatically).

    So implementing init2() would allow us to have the best of both worlds and wouldn't break any existing code.

    BTW, if you are going to add this (and it would get much =D> from me) then you might also like to think about adding the superclass as a field of all objects. That way, we'd be able to call superclass methods easily like this:
    function Derived:init2()
        self.super:init2(
        ....
        ....
    end
    best regards

    Likes: alexzheng

    +1 -1 (+1 / -0 )Share on Facebook
  • atilimatilim Maintainer
    edited January 2013
    good idea, how about the name postInit()?

    And about calling superclass function you can simply do:
    Base = Core.class(Sprite)
    Derived = Core.class(Base)
    Derived.super = Base  -- only this is needed and nothing else
     
    -- now this will work
    function Derived:init2()
        self.super:init2(
        ....
        ....
    end
    Therefore I just need to set Derived.super = Base only.
  • I often need something similar, that's why I create another method "created" which I call in init method of derived class.

    So how about "created" as a method name? :D

    But postInit also sounds nice :D
  • atilimatilim Maintainer
    edited January 2013
    "created" is also good.

    In fact, we need to choose a bit unusual name because if someone has already created a function with the same name, it'll be called twice (in that case, we shouldn't give a name as "created" :) )
  • @atilim, @ar2rsawseen, either name is fine by me. I guess postInit() is a little more unusual and therefore less likely to cause problems.

    @atilim, can you add that .super assignment to Core.class() so it is performed automatically?

    best regards
  • atilimatilim Maintainer
    Here is my original Core.class implementation:
    Core = {}
     
    Core.class = function (b)
        local c = {}
        c.__index = c
        setmetatable(c, b)
     
        if b == nil then
            c.new = function(...)
                local s1 = {}
     
                setmetatable(s1, c)
     
                local init = rawget(c, "init")
                if type(init) == "function" then
                    init(s1, ...)
                end
     
                return s1		
            end
        else
            c.new = function(...)
                local b = getmetatable(c)
     
                local s1 = b.new(...)
     
                setmetatable(s1, c)
     
                local init = rawget(c, "init")
                if type(init) == "function" then
                    init(s1, ...)
                end
     
                return s1
            end
        end
     
        return c
    end
  • While we are there, another interesting question, (maybe a more lua related) is it possible to get Class name from the instance as string? Or do a comparison as instance == typeof(Class) ?
  • atilimatilim Maintainer
    edited January 2013
    And here is the new one:
    Core = {}
     
    Core.class = function (b)
        local c = {}
        c.__index = c
        setmetatable(c, b)    
     
        c.super = b
     
        if b == nil then
            c.__new = function(...)
                local s1 = {}
     
                setmetatable(s1, c)
     
                local init = rawget(c, "init")
                if type(init) == "function" then
                    init(s1, ...)
                end
     
                return s1        
            end
        else
            c.__new = function(...)
                local b = getmetatable(c)
     
                local s1 = (b.__new or b.new)(...)
     
                setmetatable(s1, c)
     
                local init = rawget(c, "init")
                if type(init) == "function" then
                    init(s1, ...)
                end
     
                return s1
            end
        end
     
        local __new = c.__new
     
        c.new = function(...)
            local s1 = __new(...)
     
            local postInit = s1["postInit"]
            if type(postInit) == "function" then
                postInit(s1, ...)
            end
     
            return s1            
        end    
     
        return c
    end
    If you want you can simply copy-paste this code to init.lua (to replace original Core.class implementation) and test the new features.

    Likes: bowerandy

    +1 -1 (+1 / -0 )Share on Facebook
  • atilimatilim Maintainer
    @ar2rsawseen to test the instance you can do:
    getmetatable(instance) == Class
    And currently it's not possible to get a name. But you can always do:
    MyClass = Core.class()
    MyClass.name = "MyClass"
  • atilimatilim Maintainer
    And here is a simple test case:
    Base = Core.class()
    Derived = Core.class(Base)
     
    function Base:init()
    	print("Base:init")
    end
     
    function Derived:init()
    	print("Derived:init")
    end
     
    function Base:postInit()
    	print("Base:postInit")
    end
     
    function Derived:postInit()
    	self.super:postInit()
    	print("Derived:postInit")
    end
     
    Derived.new()
  • atilimatilim Maintainer
    @ar2rsawseen, @bowerandy I think both of you went to sleep :) Anyway I've committed this code and it'll available with the next version.
  • bowerandybowerandy Guru
    edited January 2013
    @ar2rsawseen, @bowerandy I think both of you went to sleep :) Anyway I've committed this code and it'll available with the next version.
    @atilim, not me!

    Before you commit this here are some more additions that you might like to consider. They add an optional classname as a second parameter to Core.class and also a "class" field to be able to know the exact class of any instance. This allows you to easily add an isKindOf() function:
    Core = {}
     
    local function isKindOf(instance, class)
    	local c=instance.class
    	while c do
    		if c==class then return true end
    		c=c.super
    	end
    	return false
    end
     
     
    Core.class = function (b, optName)
        local c = {}
        c.__index = c
        setmetatable(c, b)    
     
        c.super = b
        c.class=c
        c.__name=optName or "unknown" -- AWB added
        c.isKindOf=isKindOf -- AWB added
     
        local __new
     
        if b == nil then
            __new = function(...)
                local s1 = {}
     
                setmetatable(s1, c)
     
                local init = rawget(c, "init")
                if type(init) == "function" then
                    init(s1, ...)
                end
     
                return s1        
            end
        else
            __new = function(...)
                local b = getmetatable(c)
     
                local s1 = (b.__new or b.new)(...)
     
                setmetatable(s1, c)
     
                local init = rawget(c, "init")
                if type(init) == "function" then
                    init(s1, ...)
                end
     
                return s1
            end
        end
     
        c.__new = __new
     
        c.new = function(...)
            local s1 = __new(...)
     
            local postInit = s1["postInit"]
            if type(postInit) == "function" then
                postInit(s1, ...)
            end
     
            return s1            
        end    
     
        return c
    end
     
    -- TEST CODE BELOW
     
    Base = Core.class(nil, "Base")
    Derived = Core.class(Base, "Derived")
    Derived2= Core.class(Base, "Derived2")
     
    function Base:init()
    	print("Base:init")
    end
     
    function Derived:init()
    	print("Derived:init")
    end
     
    function Base:postInit()
    	print("Base:postInit")
    end
     
    function Derived:postInit()
    	self.super:postInit()
    	print("Derived:postInit")
    end
     
    local d=Derived.new()
    print("d isKindOf(Base)=", d:isKindOf(Base))
    print("d isKindOf(Derived2)=", d:isKindOf(Derived2))
    print("class of d is", d.class, d.class.__name)
    print("superclass of d is", d.super.class, d.super.class.__name)
    print("superclass of Derived is", Derived.super, Derived.super.__name)
    print("superclass of Base is", Base.super)
    Having the class name stored like this allows one to do an informative print of an object by overriding the __tostring metamethod. I don't really understand this but you can see how it works in MiddleClass here:

    https://github.com/kikito/middleclass/blob/master/middleclass.lua

    BTW, I forgot to thank you for adding these changes.

    best regards

    Likes: OZApps

    +1 -1 (+1 / -0 )Share on Facebook
  • atilimatilim Maintainer
    edited January 2013
    these will be great, thanks a lot. But let me add these tomorrow morning :)

    And I can add isInstanceOf also. Do you think this is a good function name for this purpose. (I couldn't explain the difference between isKindOf and isInstanceOf but you can already guess :) )
  • bowerandybowerandy Guru
    edited January 2013
    @atilim, I tried using __tostring to better describe an object when converted to a string. The problem is that you lose the table address which can be important when printing out two objects of the same class.

    With regard to isInstanceOf() I would be tempted to just add a getClass() method that returns the class object so the programmer can write:
    d:getClass()==Derived
    This is pretty readable and more flexible since the getClass() method could be used for other things. If you do want a specific testing method then I think isInstanceOf() is fine.

    best regards
  • alexzhengalexzheng Guru
    edited January 2013
    @bowerandy - I understand exactly. The current system was designed more like C++ than other OO languages. (In C++, calling a virtual function in constructor will never go to a more derived class than that of the currently executing constructor). After reading the discussion on stackoverflow, I understand that this is not the case with other OO languages.

    Currently, if Base class is registering an ENTER_FRAME event (even through another function), you cannot change/alter/override it with a Derived class.

    Let me play with Middleclass some more. I know you're a master of Smalltalk and thank you for great information.
    What @bowerandy talked about is exactly what I first thought Core.class would be.
    Now As a workaround I move all the overwritable initializtion into ADD_TO_STAGE event listener. It seems the ENTER_FRAME event listener can aslo be overided.


  • And here is a simple test case:
    Base = Core.class()
    Derived = Core.class(Base)
     
    function Base:init()
    	print("Base:init")
    end
     
    function Derived:init()
    	print("Derived:init")
    end
     
    function Base:postInit()
    	print("Base:postInit")
    end
     
    function Derived:postInit()
    	self.super:postInit()
    	print("Derived:postInit")
    end
     
    Derived.new()
    hi @atilim

    Make a little complicated code to test it.
    It still have some bug.
     
    Base = Core.class(Sprite)
    Derived = Core.class(Base)
     
    function Base:init()
    	print("Base:init", self)
    	--self:setupListener()
    end
     
    function Base:postInit()
    	print("Base:postInit", self)
    	self:setupListener()
    end
     
    function Base:setupListener()
    	print('Base:setupListener', self)
    	self:addEventListener(Event.ADDED_TO_STAGE, self.onAddtoStage, self)
    end
     
    function Base:onAddtoStage()
    	print('Base:onAddtoStage', self)
    end
     
     
    function Derived:init()
    	print("Derived:init", self)
    	--self:setupListener()
    end
     
    function Derived:postInit()
    	self.super:postInit()
    	--Base.postInit(self)
    	print("Derived:postInit", self)
    end
     
    function Derived:setupListener()
    	self:addEventListener(Event.ADDED_TO_STAGE, self.onAddtoStage, self)
    end
     
    function Derived:onAddtoStage()
    	print('Derived:onAddtoStage', self)
    end
     
    d = Derived.new()
    stage:addChild(d)
     
     
     
     
    1 
    2 self.super:someFunction() is not the same as Base.someFunction(self)
    they call on different object.
  • alexzhengalexzheng Guru
    edited January 2013
    Oh

    self.super.postInit(self)

    this works the same as Base.someFunction(self)
  • ohhhh no it looks like java :(

    :)
  • alexzhengalexzheng Guru
    edited January 2013
    this function is called on init, and do class specialized initialization.

    Why not call it onInit or onCreate, that sounds more like an auto called callback.
  • atilimatilim Maintainer
    edited January 2013
    @alexzheng oh you're right. It should be called as self.super.someFunction(self) or Base.someFunction(self) and not self.super:someFunction() :)

    onInit and onCreate are also good names. But I think I'll stick with postInit.


    @hgvyas123 it's only an extra functionality. The class system is going to work as before.


    @bowerandy if it's ok for you, let me add __tostring, isKindOf and naming of the classes to the future versions. Currently, I cannot decide how to implement them properly. Here, I think postInit was the important one and the others are a little bit optional.

    Likes: hgvyas123

    +1 -1 (+1 / -0 )Share on Facebook
  • hi @atilim
    can you clarify this state:
    Currently, if Base class is registering an ENTER_FRAME event (even through another function), you cannot change/alter/override it with a Derived class.

    But,if the ENTER_FRAME event is registered outside of the init,it can be overridden.
  • atilimatilim Maintainer
    edited January 2013

    But,if the ENTER_FRAME event is registered outside of the init,it can be overridden.
    I think it depends. Can you give a code example?
  • Base = Core.class(Sprite)
    Derived = Core.class(Base)
     
    function Base:init()
    	print("Base:init", self)
    	self:setupListener()
    end
     
    function Base:postInit()
    	print("Base:postInit", self)
    	self:setupListener()
    end
     
    function Base:setupListener()
    	print('Base:setupListener', self)
    	self:addEventListener(Event.ADDED_TO_STAGE, self.onAddtoStage, self)
    end
     
    function Base:onAddtoStage()
    	print('Base:onAddtoStage', self)
     
    	self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
    end
     
    function Base:onEnterFrame()
    	print('Base:onEnterFrame', self)
    end
     
     
    function Derived:init()
    	print("Derived:init", self)
    	--self:setupListener()
    end
     
    function Derived:postInit()
    	self.super.postInit(self)
    	--Base.postInit(self)
    	print("Derived:postInit", self)
    end
     
    function Derived:setupListener()
    	self:addEventListener(Event.ADDED_TO_STAGE, self.onAddtoStage, self)
    end
     
    function Derived:onEnterFrame()
    	print('Derived:onEnterFrame', self)
    end
    --[[
    function Derived:onAddtoStage()
    	print('Derived:onAddtoStage', self)
     
    	--self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
    end
    ]]
     
    d = Derived.new()
    stage:addChild(d)
  • bowerandybowerandy Guru
    edited January 2013
    @alexzheng,

    With the new modifications, my recommendation would be to forget that init() exists at all. If you just use postInit() instead you should find the code behaves as you requested at the head of this thread.
    Base = Core.class(Sprite)
    Derived = Core.class(Base)
     
    function Base:postInit()
    	print("Base:postInit", self)
    	self:setupListener()
    end
     
    function Base:setupListener()
    	print('Base:setupListener', self)
    	self:addEventListener(Event.ADDED_TO_STAGE, self.onAddtoStage, self)
    end
     
    function Base:onAddtoStage()
    	print('Base:onAddtoStage', self)
     
    	self:addEventListener(Event.TOUCHES_BEGIN, self.onTouchBegin, self)
    end
     
    function Base:onTouchBegin()
    	print('Base:onTouchBegin', self)
    end
     
    function Derived:postInit()
    	self.super.postInit(self)
    	print("Derived:postInit", self)
    end
     
    function Derived:onTouchBegin()
    	print('Derived:onTouchBegin', self)
    end
     
    d = Derived.new()
    stage:addChild(d)
    This gives me the following output, which is what I would expect.

    Base:postInit table: 0x8019138
    Base:setupListener table: 0x8019138
    Derived:postInit table: 0x8019138
    Base:onAddtoStage table: 0x8019138
    Derived:onTouchBegin table: 0x8019138
    Derived:onTouchBegin table: 0x8019138

    Notice how self is now the same object throughout.

    best regards
    +1 -1 (+1 / -0 )Share on Facebook
  • bowerandybowerandy Guru
    edited January 2013
    onInit and onCreate are also good names. But I think I'll stick with postInit.

    @bowerandy if it's ok for you, let me add __tostring, isKindOf and naming of the classes to the future versions. Currently, I cannot decide how to implement them properly. Here, I think postInit was the important one and the others are a little bit optional.
    I normally use the prefix "on" to signify an event handler (i.e. something that is triggered by a dispatchEvent() call). So, from my point of view, onInit() and onCreate(), wouldn't be good names.

    @atilim, sure, include what you feel happiest with. I do think including the optional classname parameter might be a good idea though. It can make debugging quite a bit easier and I can't see how it would break anything. I agree that __tostring would change quite a bit and would probably be a bad idea. isKindOf() and isInstanceOf() are nice ideas but not really essential (and testing on type is generally considered bad practice anyway).

    Actually, here's an idea. If you included a "god object" class as the root of all classes, the developer could add any of these features him/herself. I don't think this would cause any efficiency issues either.
    RootObject=Core.class()
     
    EventDispatcher=Core(RootObject)
    Then later, if I want to, I can add the following myself:
    function RootObject:isKindOf(class)
    	local c=self.class
    	while c do
    		if c==class then return true end
    		c=c.super
    	end
    	return false
    end
    This would enable lots of other extensions too, and be entirely backwards compatibile with what we have now.

    best regards

    Likes: alexzheng

    +1 -1 (+1 / -0 )Share on Facebook
Sign In or Register to comment.