我想在协议中指定一个返回值与当前实现类的类型匹配的函数。
protocol MakesSelf {
static func getInstance<T>() -> T
}
除非 T 被限制为与实现 MakesSelf 的人相同类型,例如
class MyThing: MakesSelf {
class func getInstance<T>() -> T {
return MyThing()
}
}
我的理解是编译器会根据返回值为 T 分配 MyThing 的类型,但我反而得到一个类型转换错误:Cannot convert return expression of type 'MyThing' to return type 'T'
您可以创建一个具有关联类型的协议,其中getInstance()
函数将返回该类型。在协议的扩展中创建该函数的默认实现。完整示例:
protocol Initializable {
init()
}
protocol MakesSelf: Initializable {
associatedtype Object: Initializable = Self
static func getInstance() -> Object
}
extension MakesSelf {
static func getInstance() -> Object {
return Object()
}
}
class MyThing: MakesSelf {
required init() {}
func printSelf() {
print(MyThing.self)
print(Object.self)
// Both will print "MyThing" because Object is a typealias of MyThing class
}
}
然后获取您的实例:
let instance = MyThing.getInstance()
print("(instance.self)")
/* Will print: MyTestApp.MyThing */
如您所见,因为您已经在协议扩展中提供了默认的getInstance()
实现,这在符合类中是不必要的。在您的问题的上下文中,关联的类型用作"占位符"。
我面临的问题是,在MyThing
的情况下无法推断T
。编译器在错误消息方面不是很有帮助(有一次我被告知Cannot convert return expression of type 'T' (aka 'MyImplementation') to return type 'T'
解决方案是为编译器提供某种方法来知道T
应该是什么(如果您希望它不受约束,请使用AnyObject
,而不是T
(。
就我而言,我想返回一个 Self 类型的值(实现的类型(。这比正常的 POP 问题更复杂,因为你不能在类/静态函数中引用 Self。
为了解决这个问题,我使用了 Swift 的associatedtype
,它允许我们设置一个命名占位符,以便在协议中使用。由于我们在协议级别定义它并提供一个值,因此我们可以设置对实现者自己的类型的命名引用。
associatedtype MyType = Self.Type
请注意,associatedtype MyType = Self
不起作用,因为在此上下文中,self是协议,而不是最终实现的类型(我们还不知道,我们/编译器直到某个对象实际实现协议时才知道(。
由于我们提供了一个值,因此当我们实现协议时,类型已经受到限制!由于约束仅引用实现的类型,因此实现协议足以定义类型(cool(。最好的部分是,现在我可以在我的class func
定义中消除对自我的引用:
protocol MakesSelf {
associatedtype MyType = Self.Type
static func getInstance() -> MyType
}
class MyImplementation: MakesSelf {
class func getInstance() -> MyImplementation {
print("Hello")
return MyImplementation()
}
}
let myThing = MyImplementation.getInstance()
在这里要非常清楚 - 类 MyImplementation 有一个名为 MyType 的类型别名,由协议指定。当我编译时,别名将引用当前的实现。现在,当我实现函数getInstance时,MyImplementation符合MyType。
请注意,我必须以class func getInstance() -> MyImplementation
而不是class func getInstance() -> MyType
的形式编写我的函数签名。这是因为我返回的是函数调用MyImplementation()
的结果,这恰好符合MyType的约束。这与说 MyImplementation 可以通过隐式转换为 MyType 不同。
有关尝试为返回 self 制定协议函数规范的良好阅读,请参阅此处,但我想发布具体的解决方案和解释。