86

我有一个返回对象副本的协议 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”中的方法“copy()”必须返回Self以符合协议“P”

class C除了我以finalie为前缀的情况外,没有任何效果:

final class C : P {
    func copy() -> C  {
        return C()
    }
}

但是,如果我想对 C 进行子类化,那么什么都行不通。有没有办法解决?

4

9 回答 9

152

问题是您做出了编译器无法证明您会遵守的承诺。

所以你创建了这个承诺:调用copy()将返回它自己的类型,完全初始化。

但是后来你copy()以这种方式实现了:

func copy() -> Self {
    return C()
}

现在我是一个不覆盖的子类copy()。我返回 a 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++ 并记住我们可以做出承诺。我们可以保证我们和我们所有的子类都将实现特定类型的初始化器,而 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.
  }
}

在这里,我们保证有一个初始化器为我们执行复制,然后我们可以在运行时确定调用哪个初始化器,为我们提供您正在寻找的方法语法。

于 2014-09-03T13:29:29.887 回答
26

使用 Swift 2,我们可以为此使用协议扩展。

protocol Copyable {
    init(copy:Self)
}

extension Copyable {
    func copy() -> Self {
        return Self.init(copy: self)
    }
}
于 2015-10-16T13:32:11.280 回答
19

还有另一种方法可以做你想做的事,它涉及利用 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()
于 2016-08-10T23:03:09.630 回答
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
于 2016-01-16T21:46:09.797 回答
3

Swift 5.1 now allow a forced cast to Self, 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
于 2019-11-06T04:27:48.830 回答
2

按照 Rob 的建议,这可以通过关联类型变得更通用。我稍微改变了这个例子来展示这种方法的好处。

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)
    }
}
于 2015-08-31T16:53:51.983 回答
1

我遇到了类似的问题并想出了一些可能有用的东西,所以我想分享它以供将来参考,因为这是我在寻找解决方案时发现的第一个地方之一。

如上所述,问题在于 copy() 函数的返回类型不明确。这可以通过将 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() }       
}

当返回值的类型是明确的时,这将编译并产生预期的结果。任何时候编译器必须(自己)决定返回类型应该是什么,它会发现情况模棱两可,并且对于所有实现 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() }       
}

当然,我的上下文和功能完全不同,但本着问题的精神,我试图尽可能接近给出的示例。

于 2015-08-23T08:51:06.543 回答
1

只是把我的帽子扔进这里的戒指。我们需要一个协议,该协议返回应用协议类型的可选项。我们还希望覆盖显式返回类型,而不仅仅是 Self。

诀窍不是使用“Self”作为返回类型,而是定义一个您设置为等于 Self 的关联类型,然后使用该关联类型。

这是旧方法,使用 Self ...

protocol Mappable{
    static func map() -> Self?
}

// Generated from Fix-it
extension SomeSpecificClass : Mappable{
    static func map() -> Self? {
        ...
    }
}

这是使用关联类型的新方法。请注意,返回类型现在是明确的,而不是“Self”。

protocol Mappable{
    associatedtype ExplicitSelf = Self
    static func map() -> ExplicitSelf?
}

// Generated from Fix-it
extension SomeSpecificClass : Mappable{
    static func map() -> SomeSpecificClass? {
        ...
    }
}
于 2017-11-29T19:26:21.250 回答
0

To add to the answers with the associatedtype way, I suggest to move the creating of the instance to a default implementation of the protocol extension. In that way the conforming classes won't have to implement it, thus sparing us from code duplication:

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()
于 2018-05-09T15:38:07.723 回答