【问题标题】:Using some protocol as a concrete type conforming to another protocol is not supported不支持使用某些协议作为符合另一个协议的具体类型
【发布时间】:2015-11-03 15:59:44
【问题描述】:

我正在尝试将泛型与协议混合使用,但我真的很难 xD

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

协议A

protocol ProtocolA {

}

协议 B

protocol ProtocolB : ProtocolA {

}

实施协议A

class ImplementProtocolA <P : ProtocolA> {

    let currentProtocol : P

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

}

实施协议B

class ImplementProtocolB : ImplementProtocolA<ProtocolB> {

}

所以,当我尝试将 ProtocolB 设置为实现 ProtocolA 的具体类型时,我得到了这个错误:

不支持将“ProtocolB”用作符合协议“ProtocolA”的具体类型

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 可能有意匹配“V”(又名 'GetUserView') 不符合 'View'

那是什么??符合!

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

class UserDemoPresenter : Presenter {
    typealias V = View
}

UserDemoPresenter.swift 可能有意匹配“V”(又名“视图”) 不符合“视图”

xxDD 我不明白,真的。

--已更新--

使用 Rob Napier 提出的解决方案,问题并没有得到解决,而是被延迟了。

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

private var presenter : UserDemoPresenter<GetUserView>

使用 'GetUserView' 作为符合协议的具体类型 不支持“GetUserView”

【问题讨论】:

标签: swift protocols


【解决方案1】:

限制的根本原因是 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? 以讨论如何进行类型擦除以便可以保存抽象类型。但是您需要使用具体类型。您最终无法专注于协议。在大多数情况下,您最终必须专注于具体的事情。

【讨论】:

  • 非常感谢您的解决方案和解释。您能否提供一个使用结构而不是类的代码示例,以便完全理解您的建议?
  • 我的意思是在你的代码中用单词struct 替换单词class。如果您使用struct,您将获得值语义(当您将其传递给函数时,该函数将获得自己的独立副本)。如果您需要引用语义(当您将其传递给函数时,调用者可以看到函数对其所做的修改),然后使用final classstructfinal class 都禁止子类化,这让事情变得更容易。 (子类化带来了很多疯狂。)
  • 好的,谢谢!我要做的是final类,实际上在原来的java项目中是final的。但是我想我会继续使用类而不是结构,我需要这个项目来尽可能多地模仿在 java 项目中定义的行为,而改变结构的类可能会导致一个非常非常大的不同场景 xD跨度>
  • '这是一个完全抽象的类型,Swift 没有任何规则可以将其转换为具体类型: class ImplementProtocolB : ImplementProtocolA' 嘿 Rob,你能详细说明一下这不是一个具体类型?谢谢
  • @Mercurial Swift 不知道 ProtocolB 的实际实现,因此无法计算出要分配多少存储空间来保存“您稍后将传递给我的符合 ProtocolB 但可能是任何尺寸。” Swift 可以自动创建一个间接框来存储它(这是它处理协议类型变量的方式),但这不是今天 Swift 的一部分。它需要一个具体的类型(它知道最终大小的类型)。
猜你喜欢
  • 1970-01-01
  • 2015-12-24
  • 1970-01-01
  • 2017-02-08
  • 2019-11-23
  • 1970-01-01
  • 2018-11-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多