当 Swift 中的协议遇到泛型
2016-01-30 18:13:17 来源: mengyidan1988 评论:0 点击:
Swift 中的协议如果需要泛型化,可以通过抽象类型成员的方式实现,而不是在参数类型上做文章。至此,协议本身可以不再被当成是一个类型,而是看做一个通用的约束。英文原文 问题 如果你曾将一个泛型协议当做类型使用: protocol GenericProtocol { typealias AbstractType func magic() -> AbstractType
Swift 中的协议如果需要泛型化,可以通过抽象类型成员的方式实现,而不是在参数类型上做文章。至此,协议本身可以不再被当成是一个类型,而是看做一个通用的约束。英文原文
问题
如果你曾将一个泛型协议当做类型使用:
很快 Xcode 会给你报个错:
Protocol 'GenericProtocol' can only be used as a generic constraint because it has Self or associated type requirements.
如果你不熟悉泛型,可能会奇怪,为什么泛型协议不能当做类型使用呢?答案很简单:泛型协议的名称,即:GenericProtocol 表示一组类型,并不是一个单一类型。比如你有一个关于 GenericProtocol 的随机数组,并不能确定每个元素的 magic() 方法返回的类型到底是什么,因为数组中每个元素可能都是不同的。
这个问题在 StackOverflow 也有很好的讨论
类型成员 VS 参数化
在 Swift 中,泛型化协议可以通过使用抽象类型成员来达成(使用关键字 typealias),而类、结构体、方法和函数则通过使用类型参数化来达成泛型。(观察下面的例子)
事实证明,一般情况下,只要任何语言同时支持这两种表达方式,那么类型成员和参数化最后产生的效果是一样的。我们可以想象 Swift 在未来的迭代中支持通过参数化来达成泛型,那么下面的代码可以这么写:
如果你对类型参数化和抽象类型成员之间的权衡利弊感兴趣,请看官方开发者论坛的这个帖子,以及 Scala 是如何处理这个问题的。
解决方法
我们可以使用 thunk 来解决在协议中缺少类型参数化的问题。
在实际的操作中,我们都是通过闭包来完成的:
一旦我们拥有一个 thunk,我们可以把他当做类型使用(需要我们自己提供具体的类型)
是否需要 Self
在协议的上下文声明中,Self 表示遵守协议的类型。这使得协议更加通用,self 可以被看做是针对便利类型成员的方法糖。例如:
如果我们没有能力使用 Self,可以采用下面的方式达到同样的目的:
面向协议编程
协议在 Swift 中扮演中流砥柱的角色,正如 Dave Abrahams 在 WWDC 2015 上 Session 408 中说的那样:
如果你对 Swift 中的面向协议编程感兴趣,那么我们强烈推荐去观摩完整视频。尤其下面这张幻灯片很好地阐述了『何时需要添加 Self』:
泛型和类型变异
引入泛型而来的一个问题就是泛型类型的变异(variance)。简单的说,他涉及以下问题:
我希望以后能有机会和你们探讨这个问题,建议先从维基百科这个页面 入手了解一下类型变异(主要指类型的协变和逆变 covariance-and-contravariance)
题图:『When Harry Met Sally』:)
本文转自:http://chengway.in/
问题
如果你曾将一个泛型协议当做类型使用:
protocol GenericProtocol { typealias AbstractType func magic() -> AbstractType}let list : [GenericProtocol] = []
很快 Xcode 会给你报个错:
引用
Protocol 'GenericProtocol' can only be used as a generic constraint because it has Self or associated type requirements.
如果你不熟悉泛型,可能会奇怪,为什么泛型协议不能当做类型使用呢?答案很简单:泛型协议的名称,即:GenericProtocol 表示一组类型,并不是一个单一类型。比如你有一个关于 GenericProtocol 的随机数组,并不能确定每个元素的 magic() 方法返回的类型到底是什么,因为数组中每个元素可能都是不同的。
这个问题在 StackOverflow 也有很好的讨论
类型成员 VS 参数化
在 Swift 中,泛型化协议可以通过使用抽象类型成员来达成(使用关键字 typealias),而类、结构体、方法和函数则通过使用类型参数化来达成泛型。(观察下面的例子)
func genericFunc<T>(ts : [T]) -> Bool { return true; // not very exciting}
事实证明,一般情况下,只要任何语言同时支持这两种表达方式,那么类型成员和参数化最后产生的效果是一样的。我们可以想象 Swift 在未来的迭代中支持通过参数化来达成泛型,那么下面的代码可以这么写:
protocol GenericProtocolWithParam<T> { func magic() -> T}let list : [GenericProtocolWithParam<String>] = []
如果你对类型参数化和抽象类型成员之间的权衡利弊感兴趣,请看官方开发者论坛的这个帖子,以及 Scala 是如何处理这个问题的。
解决方法
我们可以使用 thunk 来解决在协议中缺少类型参数化的问题。
- 首先定义一个结构体,该结构体实现了协议的所有方法。
- 在具体的实现方法中,再转发给(调用)『实现协议的抽象类型』。
- 在结构体的初始化过程中,这个实现了协议的抽象类型会被当做参数传入(依赖注射)
在实际的操作中,我们都是通过闭包来完成的:
引用
protocol GenericProtocol { typealias AbstractType func magic() -> AbstractType}struct GenericProtocolThunk<T> : GenericProtocol { // closure which will be used to implement `magic()` as declared in the protocol private let _magic : () -> T // `T` is effectively a handle for `AbstractType` in the protocol init<P : GenericProtocol where P.AbstractType == T>(_ dep : P) { // requires Swift 2, otherwise create explicit closure _magic = dep.magic } func magic() -> T { // any protocol methods are implemented by forwarding return _magic() }
一旦我们拥有一个 thunk,我们可以把他当做类型使用(需要我们自己提供具体的类型)
struct StringMagic : GenericProtocol { typealias AbstractType = String func magic() -> String { return "Magic!" }}// we can now create arrays of thunks if we specify the type paramlet magicians : [GenericProtocolThunk<String>] = [GenericProtocolThunk(StringMagic())] magicians.first!.magic() // returns "Magic!"
是否需要 Self
在协议的上下文声明中,Self 表示遵守协议的类型。这使得协议更加通用,self 可以被看做是针对便利类型成员的方法糖。例如:
protocol EquatableSelf { func equals(other : Self) -> Bool}// By adopting `EquatableSelf`, every occurrence of `Self` in the protocol gets// semantically replaced with `ImplicitStruct`struct ImplicitStruct : EquatableSelf { var val : Int64 func equals(other: ImplicitStruct) -> Bool { return self.val == other.val; }}
如果我们没有能力使用 Self,可以采用下面的方式达到同样的目的:
protocol EquatableTypealias { typealias EquatableType func equals(other : EquatableType) -> Bool}struct ExplicitStruct : EquatableTypealias { typealias EquatableType = ExplicitStruct var val : Int64 func equals(other: ExplicitStruct) -> Bool { return self.val == other.val; }}
面向协议编程
协议在 Swift 中扮演中流砥柱的角色,正如 Dave Abrahams 在 WWDC 2015 上 Session 408 中说的那样:
引用
... When we made Swift, we made the first protocol-oriented programming language.
如果你对 Swift 中的面向协议编程感兴趣,那么我们强烈推荐去观摩完整视频。尤其下面这张幻灯片很好地阐述了『何时需要添加 Self』:
泛型和类型变异
引入泛型而来的一个问题就是泛型类型的变异(variance)。简单的说,他涉及以下问题:
class Animal { // Animal-specific ivars + methods}class Cat : Animal { // Cat-specific ivars + methods}var cats : [Cat] = [Cat()]// Here, we're typecasting from [Cat] to [Animal].// Such typecasts could be unsafe depending on the details.var animals : [Animal] = cats
我希望以后能有机会和你们探讨这个问题,建议先从维基百科这个页面 入手了解一下类型变异(主要指类型的协变和逆变 covariance-and-contravariance)
引用
SwiftGG 也有一篇关于逆变和协变不错的科普文,通过此文我们可以知道 Swift 中的泛型是「不变的(Invariance)」
结论:optionals,arrays,dictionaries 在 Swift 中都是协变(covariant)的,而泛型以及自定义的类型都是不变的(Invariance),function 则视具体情况而定
结论:optionals,arrays,dictionaries 在 Swift 中都是协变(covariant)的,而泛型以及自定义的类型都是不变的(Invariance),function 则视具体情况而定
题图:『When Harry Met Sally』:)
本文转自:http://chengway.in/
相关热词搜索:java Swift 编程 mobile 移动开发
上一篇:一套交互设计工具推荐
下一篇:Swoole-1.8.0版本已发布,新增多项新特性
分享到:
收藏