不支持使用某些协议作为符合其他协议的具体类型

时间:2015-11-03 15:59:44

标签: swift protocols

我正在尝试将泛型与协议混合在一起,而且我很难获得xD

我在Android / Java项目中实现了某些架构,我正在尝试重写它以适应swift / iOS项目。但我发现了这个限制。

ProtocolA

protocol ProtocolA {

}

ProtocolB

protocol ProtocolB : ProtocolA {

}

ImplementProtocolA

class ImplementProtocolA <P : ProtocolA> {

    let currentProtocol : P

    init(currentProtocol : P) {
        self.currentProtocol = currentProtocol
    }

}

ImplementProtocolB

class ImplementProtocolB : ImplementProtocolA<ProtocolB> {

}

因此,当我尝试将 ProtocolB 设置为实现 ProtocolA 的具体类型时,我收到此错误:

使用&#39; ProtocolB&#39;作为符合协议的协议A&#39;不受支持

1这种“限制”有什么理由吗?

2是否有任何解决方法可以实现此目的?

3它会在某个时候得到支持吗?

- 更新 -

我认为同一问题的另一个变体是:

查看协议

protocol View {

}

protocol GetUserView : View {
    func showProgress()
    func hideProgress()
    func showError(message:String)
    func showUser(userDemo:UserDemo)
}

演示者协议

protocol Presenter {
    typealias V : View
}

class UserDemoPresenter : Presenter {
    typealias V = GetUserView
}

错误:

  

UserDemoPresenter.swift可能意图匹配&#39; V&#39; (又名   &#39; GetUserView&#39;)不符合&#39;查看'

那是什么?它符合!

即使我使用View而不是GetUserView,它也不会编译。

class UserDemoPresenter : Presenter {
    typealias V = View
}
  

UserDemoPresenter.swift可能意图匹配&#39; V&#39; (又名&#39;查看&#39;)   不符合&#39;查看&#39;

xxDD我真的不明白。

- 更新 -

根据Rob Napier提出的解决方案,问题并没有解决,相反,它只是延迟了。

当尝试定义对UserDemoPresenter的引用时,我需要指定泛型类型,所以我得到了同样的错误:

private var presenter : UserDemoPresenter<GetUserView>
  

使用&#39; GetUserView&#39;作为符合协议的具体类型   &#39; GetUserView&#39;不支持

1 个答案:

答案 0 :(得分:59)

限制的根本原因是Swift没有一流的元类型。最简单的例子是,这不起作用:

func isEmpty(xs: Array) -> Bool {
    return xs.count == 0
}

从理论上讲,这段代码可以工作,如果确实如此,那么我可以制作很多其他类型的代码(如Functor和Monad,它们今天真的无法在Swift中表达)。但你不能。你需要帮助Swift将它钉到具体类型。通常我们用泛型做到这一点:

func isEmpty<T>(xs: [T]) -> Bool {
    return xs.count == 0
}

请注意T在这里完全是多余的。我没有理由表达它;它从未使用过。但是Swift需要它,所以它可以将抽象的Array变成具体的[T]。在你的情况下也是如此。

这是一个具体的类型(嗯,它是一个抽象类型,只要它被实例化并填充P就会变成具体类型):

class ImplementProtocolA<P : ProtocolA>

这是一个完全抽象的类型,Swift没有任何规则可以变成具体类型:

class ImplementProtocolB : ImplementProtocolA<ProtocolB>

你需要使它具体化。这将编译:

class ImplementProtocolB<T: ProtocolB> : ImplementProtocolA<T> {}

还有:

class UserDemoPresenter<T: GetUserView> : Presenter {
    typealias V = T
}

只是因为您以后可能会遇到这个问题:如果您要制作这些结构或final课程,您的生活会更容易。混合协议,泛型和类多态性充满了非常尖锐的边缘。有时你很幸运,它只是不会编译。有时它会调用你不期望的东西。

您可能会对A Little Respect for AnySequence感兴趣,其中详细介绍了一些相关问题。

private var presenter : UserDemoPresenter<GetUserView>

这仍然是一种抽象类型。你的意思是:

final class Something<T: GetUserView> {
    private var presenter: UserDemoPresenter<T>
}

如果这会产生问题,则您需要创建一个框。有关如何键入 - 擦除的讨论,请参阅Protocol doesn't conform to itself?,以便您可以保留抽象类型。但是你需要在具体类型中工作。你最终不能专注于协议。在大多数情况下,你最终必须专注于具体的事情。