我在Swift中对泛型有点挣扎。
我有以下代码:
class Parent {
init(value: SomeThing) {
// ...
}
func clone<T: Parent>() -> T {
return T(value: SomeThing)
}
}
class Child : Parent {
var otherValue: SomeThingElse?
override func clone<T>() -> T where T : Parent {
let clone: Child = super.clone()
clone.otherValue = self.otherValue
return clone //ERROR: cannot convert return expression of type 'Child' to return type T
}
}
我们的想法是创建一个简单的方法,该方法返回具有相同值的子实例的新副本。 我不想为每个Child classtype编写构造函数。 (它在实际课程中有很多参数,我喜欢保持干净)。
我得到的错误是:
cannot convert return expression of type 'Child' to return type T
建议的解决方案是使其成为return clone as! T
。但是这样我就失去了使用泛型类的理由。
任何想法如何解决这个问题,同时保持它的通用性,而不是在每个类中写出构造函数?
答案 0 :(得分:3)
您需要返回类型Self
,而不是使用约束为Parent
的通用占位符。使用通用占位符,您可以说clone()
可以返回从Parent
继承的任何特定具体类型的实例。但事实并非如此 - 您只想返回相同类型的实例作为接收者,这是Self
表达的内容。
然后,您还需要实施required
初始化程序,以便可以调用所有子类,允许clone()
调用它们,而不必覆盖它们
struct Something {}
struct SomethingElse {}
class Parent {
var something: Something
required init(something: Something) {
self.something = something
}
func clone() -> Self {
// call the initialiser on the dynamic metatype of the instance,
// ensuring that we're instantiating a Self instance.
return type(of: self).init(something: something)
}
}
Child
然后的实现应该简单如下:
class Child : Parent {
var somethingElse: SomethingElse?
override func clone() -> Self {
let clone = super.clone()
clone.somethingElse = somethingElse
return clone
}
}
但遗憾的是,在clone()
上调用super
会返回一个静态输入为Parent
而不是Self
的实例 - 这已经是filed as a bug
要解决这个问题,你必须做一些强制投射hackery:
override func clone() -> Self {
let clone = super.clone() as! Child
clone.somethingElse = somethingElse
func forceCast<T>(_ value: Child) -> T { return value as! T }
return forceCast(clone)
}
嵌套的forceCast(_:)
函数可以解决这样一个问题:我们目前无法在方法中直接转换为Self
(比较Return instancetype in Swift)。在这种情况下,强制转换都将成功,因为super.clone()
将始终返回Self
实例,因此在此方法中必须为Child
。