做者:uraimo,原文连接,原文日期:2015-09-29
译者:Lanford3_3;校对:shanks;定稿:Ceegit
这篇 Swift 2.1 相关的文章须要使用 Xcode 7.1 beta 或者更新的版本, 你能够经过 GitHub 或者是 zip 文件 来获取相关 playground 文件。github
在即将和 Xcode 7.1 一块儿到来的 Swift 2.1 中(译者注:原文发表于 2015 年 9 月=,=),函数类型将支持协变与逆变。让咱们看看这意味着什么。 swift
在计算机科学及类型推断的语境中,型变(variance)这个词表示的是,两种类型之间的关系是如何影响他们派生出的复杂类型之间的关系的。复杂类型间的关系,根据原始类型间的关系来看,无外乎不变(invariance)、协变(covariance)与逆变(contravariance)。要高效地使用复杂类型,理解这种派生关系是如何定义的是很是重要的。安全
咱们用伪代码来对此进行阐释。考虑这样一个复杂的参数类型 List<T>
和两个简单类型: Car
和 Car
的一个子类型(subtype) Maserati
。函数
咱们把已有的两种类型做为 List<T>
的 T
来得到两种新类型,以后就能够经过讨论新类型的关系,来对不变,协变和逆变加以解释:ui
协变:若是 List<Maserati>
也是 List<Car>
的子类型,那么原始类型间的关系也存在于 List
中,这是由于,List
和他的原始类型是协变的。翻译
逆变:但假若 List<Car>
是 List<Maserati>
的子类型,那么原始类型间的关系和 List
派生出的复杂类型间的关系是相反的,由于 List
相对于它的原始类型是逆变的。code
不变:List<Car>
不是 List<Maserati>
的子类型,反之亦然,则两种复杂类型间没有衍生关系。ip
每种语言都采用了一个特定的型变方法集,了解复杂类型间是如何相互联系的,有助于理解两个复杂类型是否兼容,是否能在一些情境下互换,就像是一种类型和他的子类型那样。get
在函数类型(Function Types)的语境中,复杂类型的兼容问题能够归结为一个简单的问题:当你须要使用 A 类型的函数时,在什么状况下使用 B 类型的函数进行替代是安全的?
一个通用的规则是,可以兼容的函数类型有这样的特征:其参数是更加泛用的父类型(相比于 A 函数所声明的参数类型,A 的调用者也可以处理更特殊的参数),返回的结果则是一个更加特殊的子类型(A 的调用者会把返回值的类型当成 A 中声明的父类型的简化版)1,参数是逆变的,而返回值是协变的。
译者注:若是
T1
是T2
的子类型,则能够表示为T1 < T2
,那么上面的规则就能够表示为:对函数类型F1 = S1 -> T1
和F2 = S2 -> T2
来讲,当且仅当S2 < S1
且T1 < T2
时,F1
是F2
的子类型。S1
和S2
在入参位置上,他们之间的关系和F1
与F2
间的关系是相反的,因此入参是逆变的,同时,T1
和T2
在出参位置上,他们之间的关系和F1
与F2
间的关系是相同的,因此出参是协变的。
在 Swift 2.1 前的版本中,函数类型都是不变(invariance)的,若是你在 Playground 中尝试运行下面的代码,你会获得一些相似这样的警告:
// Cannot convert value of type '(Int) -> Int' to expected argument type '(Int) -> Any // (没法把 '(Int) -> Int' 转换为指望的参数类型 '(Int) -> Any') func testVariance(foo:(Int)->Any){foo(1)} func innerAnyInt(p1:Any) -> Int{ return 1 } func innerAnyAny(p1:Any) -> Any{ return 1 } func innerIntInt(p1:Int) -> Int{ return 1 } func innerIntAny(p1:Int) -> Any{ return 1 } testVariance(innerIntAny) testVariance(innerAnyInt) testVariance(innerAnyAny) testVariance(innerIntInt)
在 Swift 2.1 中状况发生了改变,Swift 已经支持函数类型转换,如今参数是逆变的,而返回值是协变的。
回到上面的示例代码,即使 testVariance
函数输入参数的类型是 Int -> Any
,但如今传入 Any -> Any
、Any -> Int
、Int -> Int
三种类型的函数也都是容许的。
译者注:上述这个 Int 和 Any 的例子其实并不合适,由于 Int 并非 Any 的子类型。能够参考下面这个例子:
class Animal {}
class Cat: Animal {}func innerAnimalCat(p1: Animal) -> Cat { return Cat() }
func innerAnimalAnimal(p1: Animal) -> Animal { return Cat() }
func innerCatCat(p1: Cat) -> Cat { return Cat() }
func innerCatAnimal(p1: Cat) -> Animal { return Cat() }func testVariance(foo: (Cat) -> Animal) { foo(Cat()) }
testVariance(innerAnimalCat)
testVariance(innerAnimalAnimal)
testVariance(innerCatCat)
testVariance(innerCatAnimal)
说点什么?来 Twitter 找我吧~
校者注:关于协变与逆变,还能够参考翻译组翻译的另一篇文章,解释的更加详细:Friday Q&A 2015-11-20:协变与逆变
我并不太理解括号中的内容。对于这段话想表达的意思,举个例子来讲明应该是,定义类型 Animal
及其子类型 Cat
,对于函数 test(catAnimalF: Cat -> Animal)
中的函数类型 A catAnimalF: Cat -> Animal
来讲,是可使用函数 B animalCatF: Animal -> Cat
来替换的。由于 animalCatF
相较于 catAnimalF
,其参数类型 Animal
是比 Cat
更加泛用的父类型,而其返回值类型则更加特殊。但做者在括号内的解释我却没看懂。第一个括号是想说,用 B 替代 A 以后,相较于 A 所声明的参数类型(Cat
),A 的调用者(test
)可以处理一个更加特殊的类型?感受不对诶...第二个括号意思是,在用 B 替代 A 后,其调用者(test
)会把返回的类型(Cat
)做为 A 中声明的返回类型(Animal
)的简化版处理?这个倒好像说的过去囧...(校者注:第一个括号的理解,A 的调用者,也就是函数 test
,函数类型的入参能够是 Cat
的父类,也就是 Animal
,译者理解是对的,第二个括号理解也是对的。)(定稿注:正好翻译了以前那篇「协变与逆变」,因此译者的理解是正确的。)
本文由 SwiftGG 翻译组翻译,已经得到做者翻译受权,最新文章请访问 http://swift.gg。