我可以根据返回值使用set占位符类型吗?

时间:2018-05-09 13:28:01

标签: ios swift generics protocols swift-protocols

我想在协议中指定一个函数,其返回值与当前实现类的类型匹配。

var foos = document.body.querySelectorAll(".foo");
foos.forEach(function (item) {            
    item.addEventListener("click", () => { 
        console.log(item); 
    });
});

除非T被约束为实现MakesSelf的同类型,例如

protocol MakesSelf {
    static func getInstance<T>() -> T
}

我理解编译器会根据返回值为T分配MyThing的类型,但我得到的是类型转换错误:class MyThing: MakesSelf { class func getInstance<T>() -> T { return MyThing() } }

2 个答案:

答案 0 :(得分:2)

您可以创建一个具有关联类型的协议,其中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()的默认实现,而这在合规类中是不必要的。在您的问题的上下文中,关联类型充当“占位符”。

答案 1 :(得分:0)

我遇到的问题是在T的情况下无法推断MyThing。当涉及到错误消息时,编译器并不是非常有帮助(有一次我被告知Cannot convert return expression of type 'T' (aka 'MyImplementation') to return type 'T'

解决方案是为编译器提供一些方法来了解T应该是什么(如果你想要它不受限制,使用AnyObject,而不是T)。

在我的情况下,我想返回Self类型的值(实现&#39; s类型)。这比正常的POP问题更复杂,因为你不能在类/静态函数中引用Self。

为了解决这个问题,我使用了Swift的associatedtype,它允许我们设置一个命名的占位符,以便在协议中使用。由于我们在协议级别定义了这个并提供了一个值,因此我们可以设置一个对实现者自己的类型的命名引用。

associatedtype MyType = Self.Type 

注意 associatedtype MyType = Self不起作用,因为此上下文中的self 是协议,而不是最终实现的类型(我们不知道#39;我们知道,我们/编译器不知道,直到某个对象实际实现了协议。)

由于我们提供了一个值,当我们实现我们的协议时,类型已经受到约束!由于约束仅涉及实现的类型,因此实现协议足以定义类型(酷)。最好的部分是现在我可以在我的class func定义中删除引用Self:

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的说法不同。

关于尝试制作protocol function specification for returning self see here的好读物,但我想发布具体的解决方案和解释。