拷贝构造函数与“Cannot call value of non-function type”错误及子类拷贝方案
问题解答
1. 代码错误的原因
你写的copy()方法有两个关键问题导致了编译错误:
- 缺少返回值类型:你的方法声明是
func copy() { ... },默认返回Void(无返回值),但你在方法内部写了return语句,这本身就违背了方法的定义,编译器无法理解你试图返回什么类型的值。 - 类型推断失败:即使忽略返回值的问题,
type(of: self).init(self)的调用也会因为编译器无法正确解析目标类型而报错。因为没有明确的返回类型约束,编译器无法将type(of: self)(实际是子类的元类型)的初始化器调用和方法的返回预期关联起来,最终把init误判为非函数类型,抛出"Cannot call value of non-function type 'AType'"的错误。
简单来说,就是你没告诉编译器这个copy()方法要返回什么类型,导致它完全搞不懂你想做的初始化调用逻辑。
2. 更简便的实现方式
其实只需要修正基类的copy()方法,让它明确返回Self类型,配合你已经定义的required初始化器,就能让所有子类自动支持拷贝功能,不需要额外代码:
class A { var param1: Int // 假设param1是Int类型,可根据实际情况调整 // 普通初始化器 init(param1: Int) { self.param1 = param1 } // 你已经定义的required拷贝初始化器 required init(m: A) { self.param1 = m.param1 } // 修正后的copy方法 func copy() -> Self { return type(of: self).init(self) } } class X: A {} class Y: A {} class Z: A {}
使用的时候非常直接:
let x = X(param1: 10) let xCopy = x.copy() // xCopy的类型是X,参数和x完全一致 let y = Y(param1: 20) let yCopy = y.copy() // yCopy的类型是Y
这种方式的核心优势:
- 基类只需要写一次逻辑,所有子类自动继承拷贝能力
- 返回的
Self类型保证了拷贝后的实例和原实例类型完全一致,不需要额外类型转换 - 依赖你已经实现的
required初始化器,不需要重复编写拷贝逻辑
如果子类有额外属性需要拷贝,只需要在子类中重写required init(m: A)补充属性拷贝即可,copy()方法依然可以直接继承使用:
class X: A { var param2: String init(param1: Int, param2: String) { self.param2 = param2 super.init(param1: param1) } required init(m: A) { guard let x = m as? X else { fatalError("Cannot copy non-X instance to X") } self.param2 = x.param2 super.init(m: x) } }
这样X的实例调用copy()时,会正确拷贝param1和param2两个属性。
内容的提问来源于stack exchange,提问作者Opus1217
相关产品推荐
相关产品推荐

