我有一个协议P,它返回一个对象的副本:
protocol P {
func copy() -> Self
}
和实现P的C类:
class C : P {
func copy() -> Self {
return C()
}
}
但是,我是否将返回值设为Self
我收到以下错误:
无法转换类型' C'的返回表达式返回类型' Self'
我也尝试过返回C
。
class C : P {
func copy() -> C {
return C()
}
}
导致以下错误:
方法'复制()'在非最终班级' C'必须返回
Self
才能符合 协议' P'
除了class C
前缀为final
的情况外,没有任何作用,即:
final class C : P {
func copy() -> C {
return C()
}
}
但是如果我想要子类C那么什么都行不通。有没有办法解决这个问题?
答案 0 :(得分:135)
问题在于你承诺编译器无法证明你会保留。
所以你创造了这个承诺:调用copy()
将返回自己的类型,完全初始化。
但是你以这种方式实施了copy()
:
func copy() -> Self {
return C()
}
现在我是一个不会覆盖copy()
的子类。我返回C
,而不是完全初始化Self
(我承诺过)。这样做并不好。怎么样:
func copy() -> Self {
return Self()
}
嗯,那不会编译,但即使这样做,也不会有好处。子类可能没有简单的构造函数,因此D()
可能甚至不合法。 (虽然见下文。)
好的,那怎么样:
func copy() -> C {
return C()
}
是的,但这不会返回Self
。它返回C
。你还没有履行诺言。
"但是ObjC可以做到这一点!"好吧,有点。主要是因为如果你按照斯威夫特的方式履行承诺,它并不在乎。如果未能在子类中实现copyWithZone:
,则可能无法完全初始化对象。编译器甚至不会警告你,你已经做到了。
"但ObjC中的大部分内容都可以翻译成Swift,ObjC可以NSCopying
。"是的,确实如此,
func copy() -> AnyObject!
所以你也可以这样做(这里没有理由!)
protocol Copyable {
func copy() -> AnyObject
}
那说"我对你得到的东西没有任何承诺。"你也可以说:
protocol Copyable {
func copy() -> Copyable
}
这是你可以作出的承诺。
但我们可以暂时考虑一下C ++,并记住我们可以做出的承诺。我们可以保证我们和我们所有的子类都将实现特定类型的初始化器,Swift将强制执行(因此可以证明我们说的是实话):
protocol Copyable {
init(copy: Self)
}
class C : Copyable {
required init(copy: C) {
// Perform your copying here.
}
}
这就是你应该如何进行复制。
我们可以更进一步,但它使用dynamicType
,我还没有对其进行广泛测试,以确保它始终是我们想要的,但它应该是正确的:
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.
}
}
这里我们保证有一个初始化程序为我们执行复制,然后我们可以在运行时确定要调用哪一个,为我们提供您正在寻找的方法语法。
答案 1 :(得分:24)
使用Swift 2,我们可以使用协议扩展。
protocol Copyable {
init(copy:Self)
}
extension Copyable {
func copy() -> Self {
return Self.init(copy: self)
}
}
答案 2 :(得分:12)
还有另一种方法可以做你想要的,包括利用Swift的相关类型。这是一个简单的例子:
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()
答案 3 :(得分:10)
实际上,当协议(gist)要求时,有一种技巧可以轻松地返回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
答案 4 :(得分:2)
根据Rob的建议,这可以通过associated types更加通用。我已经改变了一些例子来证明这种方法的好处。
protocol Copyable: NSCopying {
associatedtype 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)
}
}
答案 5 :(得分:1)
我遇到了类似的问题并想出了一些可能有用的内容,所以我会分享它以供将来参考,因为这是我在搜索解决方案时找到的第一个地方。
如上所述,问题是copy()函数的返回类型的模糊性。通过分离副本() - &gt;可以非常清楚地说明这一点。 C和copy() - &gt; P函数:
因此,假设您按如下方式定义协议和类:
protocol P
{
func copy() -> P
}
class C:P
{
func doCopy() -> C { return C() }
func copy() -> C { return doCopy() }
func copy() -> P { return doCopy() }
}
当返回值的类型显式时,这会编译并生成预期结果。每当编译器必须决定返回类型应该是什么时(它自己),它将发现情况不明确并且对于实现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()函数的情况下工作,或者你总是有明确的类型上下文。
我发现使用与为各种笨重代码制作的具体类相同的函数名,所以我最终为协议的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() }
}
当然,我的背景和功能完全不同,但在问题的精神上,我尽量保持尽可能接近的例子。
答案 6 :(得分:0)
把帽子扔到戒指里。我们需要一个协议,返回应用协议类型的可选项。我们还希望覆盖显式返回类型,而不仅仅是Self。
技巧不是使用'Self'作为返回类型,而是定义一个您设置的相关类型,等于Self,然后使用该关联类型。
这是旧的方式,使用Self ...
protocol Mappable{
static func map() -> Self?
}
// Generated from Fix-it
extension SomeSpecificClass : Mappable{
static func map() -> Self? {
...
}
}
以下是使用关联类型的新方法。请注意,返回类型现在是显式的,而不是“自我”。
protocol Mappable{
associatedtype ExplicitSelf = Self
static func map() -> ExplicitSelf?
}
// Generated from Fix-it
extension SomeSpecificClass : Mappable{
static func map() -> SomeSpecificClass? {
...
}
}
答案 7 :(得分:0)
要使用associatedtype
方式添加答案,我建议将实例的创建移至协议扩展的默认实现。通过这种方式,符合标准的类不必实现它,从而使我们免于代码重复:
protocol Initializable {
init()
}
protocol Creatable: Initializable {
associatedtype Object: Initializable = Self
static func newInstance() -> Object
}
extension Creatable {
static func newInstance() -> Object {
return Object()
}
}
class MyClass: Creatable {
required init() {}
}
class MyOtherClass: Creatable {
required init() {}
}
// Any class (struct, etc.) conforming to Creatable
// can create new instances without having to implement newInstance()
let instance1 = MyClass.newInstance()
let instance2 = MyOtherClass.newInstance()
答案 8 :(得分:0)
Swift 5.1现在允许强制将自己强制转换为as! Self
1> protocol P {
2. func id() -> Self
3. }
9> class D : P {
10. func id() -> Self {
11. return D()
12. }
13. }
error: repl.swift:11:16: error: cannot convert return expression of type 'D' to return type 'Self'
return D()
^~~
as! Self
9> class D : P {
10. func id() -> Self {
11. return D() as! Self
12. }
13. } //works