协议func返回自我
我有一个协议P,返回对象的副本:
protocol P { func copy() -> Self }
和一个C类实现P:
class C : P { func copy() -> Self *or* C { return C() } }
但是,无论我把返回值设置为C还是作为Self,除了以class C
为前缀class C
的情况外,没有任何工作。 但是如果我想C的子类,那么什么都不会工作。 有没有办法解决?
问题是,你正在作出承诺,编译器不能certificate你会保持。
所以你创造了这个承诺:调用copy()
将返回它自己的types,完全初始化。
但是,你这样实现copy()
:
func copy() -> Self { return C() }
现在我是一个不覆盖copy()
的子类。 我返回一个C
,而不是一个完全初始化的Self
(我承诺过)。 所以这不好。 怎么样:
func copy() -> Self { return Self() }
那么,这不会编译,但即使这样做,也不会有好处。 子类可能没有简单的构造函数,所以D()
可能不合法。 (虽然见下文)
好的,那么怎么样:
func copy() -> C { return C() }
是的,但不会返回Self
。 它返回C
你还没有遵守你的承诺。
“但是ObjC可以做到!” 那么,有点。 主要是因为它不在乎你是否像Swift一样遵守诺言。 如果在子类中未能实现copyWithZone:
你可能无法完全初始化你的对象。 编译器甚至不会警告你已经这样做了。
“但是ObjC中的大部分内容都可以被转换成Swift,而ObjC已经可以被NSCopying了。” 是的,这是如何定义的:
func copy() -> AnyObject!
所以你可以做同样的事(这里没有理由!):
protocol Copyable { func copy() -> AnyObject }
那就是说:“我不承诺你回报什么。” 你也可以说:
protocol Copyable { func copy() -> Copyable }
这是你可以做出的承诺。
但是我们可以稍微思考一下C ++,记住我们可以做出的承诺。 我们可以承诺,我们和我们所有的子类将实现特定types的初始化器,而Swift将强制执行(因此可以certificate我们说的是事实):
protocol Copyable { init(copy: Self) } class C : Copyable { required init(copy: C) { // Perform your copying here. } }
这就是你应该如何执行副本。
我们可以进一步采取这一步,但它使用dynamicType
,我没有广泛地testing它,以确保它始终是我们想要的,但它应该是正确的:
protocol Copyable { func copy() -> Self init(copy: Self) } class C : Copyable { func copy() -> Self { return self.dynamicType(copy: self) } required init(copy: C) { // Perform your copying here. } }
这里我们保证有一个初始化器为我们执行拷贝,然后我们可以在运行时确定要调用哪一个,给我们你正在寻找的方法语法。
使用Swift 2,我们可以使用协议扩展。
protocol Copyable { init(copy:Self) } extension Copyable { func copy() -> Self { return Self.init(copy: self) } }
实际上,有一个技巧可以在协议要求的情况下方便地返回Self
:
/// Cast the argument to the infered function return type. func autocast<T>(some: Any) -> T? { return some as? T } protocol Foo { static func foo() -> Self } class Vehicle: Foo { class func foo() -> Self { return autocast(Vehicle())! } } class Tractor: Vehicle { override class func foo() -> Self { return autocast(Tractor())! } } func typeName(some: Any) -> String { return (some is Any.Type) ? "\(some)" : "\(some.dynamicType)" } let vehicle = Vehicle.foo() let tractor = Tractor.foo() print(typeName(vehicle)) // Vehicle print(typeName(tractor)) // Tractor
根据Rob的build议,这可以通过关联types变得更通用。 我已经改变了这个例子来展示这个方法的好处。
protocol Copyable, NSCopying { typealias Prototype init(copy: Prototype) init(deepCopy: Prototype) } class C : Copyable { typealias Prototype = C // <-- requires adding this line to classes required init(copy: Prototype) { // Perform your copying here. } required init(deepCopy: Prototype) { // Perform your deep copying here. } @objc func copyWithZone(zone: NSZone) -> AnyObject { return Prototype(copy: self) } }
还有另外一种方法来做你想做的事情,包括利用Swift的关联types。 这是一个简单的例子:
public protocol Creatable { associatedtype ObjectType = Self static func create() -> ObjectType } class MyClass { // Your class stuff here } extension MyClass: Creatable { // Define the protocol function to return class type static func create() -> MyClass { // Create an instance of your class however you want return MyClass() } } let obj = MyClass.create()
我有类似的问题,并提出了一些可能有用的东西,所以我虽然我会分享它的未来参考,因为这是我find一个解决scheme时发现的第一个地方之一。
如上所述,问题是copy()函数返回types的含糊不清。 通过分离copy() – > C和copy() – > P函数可以很清楚地说明这一点:
所以,假设你定义协议和类如下:
protocol P { func copy() -> P } class C:P { func doCopy() -> C { return C() } func copy() -> C { return doCopy() } func copy() -> P { return doCopy() } }
当返回值的types是显式的时,这将编译并产生预期的结果。 任何时候编译器必须决定返回types应该是什么(它自己),它会发现情况不明确,并且对于实现P协议的所有具体类都会失败。
例如:
var aC:C = C() // aC is of type C var aP:P = aC // aP is of type P (contains an instance of C) var bC:C // this to test assignment to a C type variable var bP:P // " " " P " " bC = aC.copy() // OK copy()->C is used bP = aC.copy() // Ambiguous. // compiler could use either functions bP = (aC as P).copy() // but this resolves the ambiguity. bC = aP.copy() // Fails, obvious type incompatibility bP = aP.copy() // OK copy()->P is used
总之,这可以在你使用基类的copy()函数或者总是有显式types上下文的情况下工作。
我发现,使用相同的函数名称作为具体类的笨拙代码无处不在,所以我最终使用协议的copy()函数不同的名称。
最终结果更像是:
protocol P { func copyAsP() -> P } class C:P { func copy() -> C { // there usually is a lot more code around here... return C() } func copyAsP() -> P { return copy() } }
当然,我的背景和function是完全不同的,但是本着这个问题的精神,我尽量保持尽可能接近的范例。