一、什么是多态
多态其实就是子类重写父类的函数,在运行时,如果通过父类对象调用函数,实际上会根据子类的实际类型调用子类中重写的函数。
二、在父类中定义函数
BP_WeaponBase_C.lua
--父类构造函数
function BP_WeaponBase_C:UserConstructionScript()print("BP_WeaponBase_C:UserConstructionScript")print(tostring(self))self:SpawnBall() --直接模拟外部调用父类的SpawnBall函数
end
--父类生成球函数
function BP_WeaponBase_C:SpawnBall()print("BP_WeaponBase_C:SpawnBall")print(tostring(self))return nil
end
三、子类如何实现多态?
看下面3种情况,但只有一种情况下是对的!
BP_DefaultWeapon_C.lua
1、情况一:子类构造函数不调用父类构造函数。
--子类继承父类
local BP_DefaultWeapon_C = UnLua.Class("BP_WeaponBase")
--子类构造函数
function BP_DefaultWeapon_C:UserConstructionScript()print("BP_DefaultWeapon_C:UserConstructionScript")print(tostring(self))
end
--重写父类生成球函数
function BP_DefaultWeapon_C:SpawnBall()print("BP_DefaultWeapon_C:SpawnBall")print(tostring(self))
end
打印
只调用子类的构造函数,并没有调用父类构造函数,所以SpawnBall也没有调用
很明显,连父类构造函数都没有调用,并没有实现多态!
2、情况二:子类构造函数用冒号调用父类构造函数。
在子类的构造函数中,添加self.Super:UserConstructionScript()代码
--子类构造函数
function BP_DefaultWeapon_C:UserConstructionScript()print("BP_DefaultWeapon_C:UserConstructionScript")print(tostring(self))self.Super:UserConstructionScript()
end
打印
先调用子类的构造函数,子类的self地址是00000BADB6066700
再调用父类的构造函数,父类的self地址是00000BADB6068600,与子类的self不一样
最后调用父类的SpawnBall,传进去的self是00000BADB6068600,即父类的self
虽然调用了父类构造函数,但是当执行self:SpawnBall()时,这里的self是父类的self,所以调用的是父类的函数而不是子类所重写的函数,也没有实现多态!
3、情况三:子类构造函数用点号调用父类构造函数,并将子类的self作为第一个参数传进去。
在子类的构造函数中,修改代码如下:
function BP_DefaultWeapon_C:UserConstructionScript()print("BP_DefaultWeapon_C:UserConstructionScript")print(tostring(self))self.Super.UserConstructionScript(self)
end
打印
先调用子类的构造函数,子类的self地址是00000BAD9C969F80
再调用父类的构造函数,父类的self地址是00000BAD9C969F80,即子类的self
最后调用子类的SpawnBall,传进去的self是00000BAD9C969F80,也是子类的self
显而易见,这种调用方式才是UnLua实现多态的正确姿势,在子类中通过点号调用父类构造函数,并把子类的self当作参数传给父类,这样才能在执行self:SpawnBall()时,确保self是子类的self,执行的也是子类的函数!