Swift中父类构造方法调用自身函数为何触发子类重写实现?
问题原因
这个现象是Swift面向对象的动态派发机制决定的,核心逻辑如下:
- 类的普通实例方法默认采用动态派发,调用时会查找当前实例的实际运行时类型对应的方法实现,和代码写在哪个类的代码块里没有关系
- 你调用
Vegan()创建实例时,实例的实际类型已经确定为Vegan,构造方法只是完成实例的属性初始化工作,不会改变实例本身的类型 - 父类
Human的init执行时,操作的就是这个类型为Vegan的实例,调用eat()时自然会查到Vegan类重写后的实现,输出No meat or dairy!!
误区解释
你认为「父类init代码块的逻辑在自身上下文执行,应该调用父类方法」是对构造过程的误解:
父类构造器的执行上下文绑定的是当前正在构造的子类实例,而不是独立的父类实例,所有实例方法调用都会遵循动态派发规则走实际类型的实现。
实现预期效果的方案
如果你希望父类init中调用的eat()固定走父类自己的实现,有两种常用方案:
- 给父类的
eat()方法添加final修饰符,禁止子类重写,此时方法会改为静态派发,固定调用父类实现:
class Human { init() { eat() } // 加final禁止子类重写 final func eat() { print("I eat edible stuff") } }
- 若需要保留子类重写
eat()的能力,只是父类构造器中要调用自己的实现,可以把父类内部调用的逻辑拆分为私有方法(私有方法默认静态派发,不会被子类重写):
class Human { init() { privateEat() } private func privateEat() { print("I eat edible stuff") } func eat() { privateEat() } } class Vegan: Human { override func eat() { print("No meat or dairy!!") } }
注意事项
在构造器中调用可被子类重写的实例方法属于危险操作:此时子类的存储属性还没有完成初始化,如果子类重写的方法中访问了自身的未初始化属性,会直接触发运行时崩溃,日常开发中应当尽量避免这种写法。
内容的提问来源于stack exchange,提问作者ImShrey
相关产品推荐
相关产品推荐

