Protocol func返回Self

aeu*_*nks 70 protocols subclassing swift swift-protocols

我有一个协议P,它返回一个对象的副本:

protocol P {
    func copy() -> Self
}
Run Code Online (Sandbox Code Playgroud)

以及实现P的C类:

class C : P {
    func copy() -> Self {
        return C()
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,我是否输入返回值,因为Self我得到以下错误:

无法将类型为"C"的返回表达式转换为返回类型"Self"

我也试过回来了C.

class C : P {
    func copy() -> C  {
        return C()
    }
}
Run Code Online (Sandbox Code Playgroud)

这导致以下错误:

非终结类'C'中的方法'copy()'必须返回Self以符合协议'P'

没有什么可行的,除了我前缀class Cfinalie 的情况:

final class C : P {
    func copy() -> C  {
        return C()
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我想继承C,那么什么都行不通.有没有办法解决?

Rob*_*ier 140

问题是你承诺编译器无法证明你会保留.

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

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

func copy() -> Self {
    return C()
}
Run Code Online (Sandbox Code Playgroud)

现在我是一个不会覆盖的子类copy().而且我返回了一个C,而不是一个完全初始化的Self(我承诺过).所以这不好.怎么样:

func copy() -> Self {
    return Self()
}
Run Code Online (Sandbox Code Playgroud)

好吧,那不会编译,但即使它确实如此,也没有好处.子类可能没有普通的构造函数,所以D()甚至可能不合法.(虽然见下文.)

好吧,那怎么样:

func copy() -> C {
    return C()
}
Run Code Online (Sandbox Code Playgroud)

是的,但这不会回来Self.它回来了C.你还没有履行诺言.

"但是ObjC可以做到!" 好吧,有点.主要是因为它并不关心你是否像Swift那样遵守诺言.如果未能copyWithZone:在子类中实现,则可能无法完全初始化对象.编译器甚至不会警告你,你已经这样做了.

"但ObjC中的大部分内容都可以翻译成Swift,ObjC也可以NSCopying." 是的确如此,以下是它的定义:

func copy() -> AnyObject!
Run Code Online (Sandbox Code Playgroud)

所以你也可以这样做(这里没有理由!)

protocol Copyable {
  func copy() -> AnyObject
}
Run Code Online (Sandbox Code Playgroud)

这说:"我对你得到的东西没有任何承诺." 你也可以说:

protocol Copyable {
  func copy() -> Copyable
}
Run Code Online (Sandbox Code Playgroud)

这是你可以做出的承诺.

但我们可以暂时考虑一下C++并记住我们可以做出的承诺.我们可以保证我们和我们所有的子类都将实现特定类型的初始化器,Swift将强制执行(因此可以证明我们说实话):

protocol Copyable {
  init(copy: Self)
}

class C : Copyable {
  required init(copy: C) {
    // Perform your copying here.
  }
}
Run Code Online (Sandbox Code Playgroud)

这就是你应该如何进行复制.

我们可以更进一步,但它使用了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.
  }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 因为它们都需要实现`初始化的最后一个纯斯威夫特解决方案不与子类工作(副本:C)``而不是初始化(复印件:自)`:( (2认同)

Tol*_*kur 24

使用Swift 2,我们可以使用协议扩展.

protocol Copyable {
    init(copy:Self)
}

extension Copyable {
    func copy() -> Self {
        return Self.init(copy: self)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案.它可以简化为`return Self(copy:self)`(至少在Swift 2.2中). (2认同)

小智 14

还有另一种方法可以做你想要的,包括利用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()
Run Code Online (Sandbox Code Playgroud)


wer*_*ver 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
Run Code Online (Sandbox Code Playgroud)


joh*_*nvc 5

Swift 5.1 现在允许强制转换为 Self,as! Self

给定

protocol P { 
     func id() -> Self 
 } 
Run Code Online (Sandbox Code Playgroud)

这有效:

 class D : P { 
     func id() -> Self { 
         return D() as! Self
     } 
 }
Run Code Online (Sandbox Code Playgroud)

如果没有as! Self,您会收到错误:

error: repl.swift:11:16: error: cannot convert return expression of type 'D' to return type 'Self'
        return D()
               ^~~
                   as! Self
Run Code Online (Sandbox Code Playgroud)