Swift中创建子类时如何避免属性定义重复?
Swift子类初始化时重复父类参数是否必须?
我在以下Swift代码中发现,父类Beverage和子类Beer的init()方法都要声明name和form参数,这显得冗余。子类的核心优势应该是继承父类属性,重复声明这些参数好像违背了继承的设计初衷,而且每次调用super.init也很繁琐。尝试移除冗余代码后程序无法正常运行,请问我必须接受这种冗余写法吗?
class Beverage { var name: String var form: String init(name: String = "unknown", form: String = "liquid") { self.name = name self.form = form } } class Beer: Beverage { var alcohol: Double? init(name: String = "unknown", form: String = "liquid", alcohol: Double?) { self.alcohol = alcohol super.init(name: name, form: form) } } let myBev = Beer(name: "Heineken", alcohol: 0.05) if let alcohol = myBev.alcohol { print("I'm drinking a glass of \(myBev.name), which is a \(myBev.form) and has \(alcohol)% alcohol.") } else { print("I'm drinking a glass of \(myBev.name), which is a \(myBev.form).") } // prints "I'm drinking a glass of Heineken, which is a liquid and has 0.05% alcohol."
解答
不用接受这种冗余写法,有几种实用的优化方式:
1. 使用便捷初始化器(Convenience Initializer)
给子类添加便捷初始化器,只处理子类独有的参数,然后调用父类的指定初始化器,这样就不用重复声明父类的参数和默认值:
class Beer: Beverage { var alcohol: Double? // 便捷初始化器,标记convenience convenience init(alcohol: Double?, name: String = "unknown", form: String = "liquid") { // 先调用父类的指定初始化器 self.init(name: name, form: form) // 再初始化子类独有的属性 self.alcohol = alcohol } } // 使用方式不变 let myBev = Beer(name: "Heineken", alcohol: 0.05)
2. 给子类属性设置默认值
如果子类的alcohol属性不需要在初始化时必须赋值,可以直接在属性定义时设置默认值,这样子类不需要自定义任何初始化器,完全继承父类的初始化逻辑:
class Beer: Beverage { // 给alcohol设置默认值nil var alcohol: Double? = nil } // 使用时先初始化父类属性,再赋值子类属性 let myBev = Beer(name: "Heineken") myBev.alcohol = 0.05
这种方式最简洁,适合子类属性非必填的场景。
3. 调整初始化器参数顺序
如果一定要自定义指定初始化器,可以把子类独有的参数放在前面,逻辑上更清晰,同时保留父类参数的默认值,避免冗余感:
class Beer: Beverage { var alcohol: Double? init(alcohol: Double?, name: String = "unknown", form: String = "liquid") { self.alcohol = alcohol super.init(name: name, form: form) } } // 使用时可以更直观地传递子类参数 let myBev = Beer(alcohol: 0.05, name: "Heineken")
为什么移除冗余代码会报错?
Swift的初始化规则要求:子类的指定初始化器必须调用父类的指定初始化器,确保父类的所有存储属性都被正确初始化。父类Beverage的name和form没有属性级别的默认值(初始化器的默认值不算属性本身的默认值),如果子类初始化器不传递这两个参数,父类的属性无法被赋值,编译器就会报错。
只要满足父类存储属性都被初始化的要求,就能通过上述方式减少代码冗余。
内容的提问来源于stack exchange,提问作者identic0n
相关产品推荐
相关产品推荐

