作者:uraimo享甸,原文鏈接,原文日期:2015-09-29
譯者:Lanford3_3;校對:shanks就珠;定稿:Cee
這篇 Swift 2.1 相關的文章需要使用 Xcode 7.1 beta 或者更新的版本, 你可以通過 GitHub 或者是 zip 文件 來獲取相關 playground 文件醒颖。
在即將和 Xcode 7.1 一起到來的 Swift 2.1 中(譯者注:原文發(fā)表于 2015 年 9 月=妻怎,=),函數(shù)類型將支持協(xié)變與逆變泞歉。讓我們看看這意味著什么逼侦。
在計算機科學及類型推斷的語境中,型變(variance)這個詞表示的是腰耙,兩種類型之間的關系是如何影響他們派生出的復雜類型之間的關系的榛丢。復雜類型間的關系,根據(jù)原始類型間的關系來看沟优,無外乎不變(invariance)涕滋、協(xié)變(covariance)與逆變(contravariance)。要高效地使用復雜類型挠阁,理解這種派生關系是如何定義的是非常重要的宾肺。
我們用偽代碼來對此進行闡釋溯饵。考慮這樣一個復雜的參數(shù)類型 List<T>
和兩個簡單類型: Car
和 Car
的一個子類型(subtype) Maserati
锨用。
我們把已有的兩種類型作為 List<T>
的 T
來獲得兩種新類型丰刊,之后就可以通過討論新類型的關系,來對不變增拥,協(xié)變和逆變加以解釋:
-
協(xié)變:如果
List<Maserati>
也是List<Car>
的子類型啄巧,那么原始類型間的關系也存在于List
中,這是因為掌栅,List
和他的原始類型是協(xié)變的秩仆。 -
逆變:但倘若
List<Car>
是List<Maserati>
的子類型,那么原始類型間的關系和List
派生出的復雜類型間的關系是相反的猾封,因為List
相對于它的原始類型是逆變的澄耍。 -
不變:
List<Car>
不是List<Maserati>
的子類型,反之亦然晌缘,則兩種復雜類型間沒有衍生關系齐莲。
每種語言都采用了一個特定的型變方法集,了解復雜類型間是如何相互聯(lián)系的磷箕,有助于理解兩個復雜類型是否兼容选酗,是否能在一些情境下互換,就像是一種類型和他的子類型那樣岳枷。
在函數(shù)類型(Function Types)的語境中芒填,復雜類型的兼容問題可以歸結(jié)為一個簡單的問題:當你需要使用 A 類型的函數(shù)時,在什么情況下使用 B 類型的函數(shù)進行替代是安全的嫩舟?
一個通用的規(guī)則是氢烘,能夠兼容的函數(shù)類型有這樣的特征:其參數(shù)是更加泛用的父類型(相比于 A 函數(shù)所聲明的參數(shù)類型,A 的調(diào)用者也能夠處理更特殊的參數(shù))家厌,返回的結(jié)果則是一個更加特殊的子類型(A 的調(diào)用者會把返回值的類型當成 A 中聲明的父類型的簡化版)[^1]播玖,參數(shù)是逆變的,而返回值是協(xié)變的饭于。
譯者注:如果
T1
是T2
的子類型蜀踏,則可以表示為T1 < T2
,那么上面的規(guī)則就可以表示為:對函數(shù)類型F1 = S1 -> T1
和F2 = S2 -> T2
來說掰吕,當且僅當S2 < S1
且T1 < T2
時果覆,F1
是F2
的子類型。
S1
和S2
在入?yún)⑽恢蒙现呈欤麄冎g的關系和F1
與F2
間的關系是相反的局待,所以入?yún)⑹悄孀兊模瑫r,T1
和T2
在出參位置上钳榨,他們之間的關系和F1
與F2
間的關系是相同的舰罚,所以出參是協(xié)變的。
在 Swift 2.1 前的版本中薛耻,函數(shù)類型都是不變(invariance)的营罢,如果你在 Playground 中嘗試運行下面的代碼,你會得到一些類似這樣的警告:
// Cannot convert value of type '(Int) -> Int' to expected argument type '(Int) -> Any
// (無法把 '(Int) -> Int' 轉(zhuǎn)換為期望的參數(shù)類型 '(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 中情況發(fā)生了改變饼齿,Swift 已經(jīng)支持函數(shù)類型轉(zhuǎn)換饲漾,現(xiàn)在參數(shù)是逆變的,而返回值是協(xié)變的缕溉。
回到上面的示例代碼考传,即便 testVariance
函數(shù)輸入?yún)?shù)的類型是 Int -> Any
,但現(xiàn)在傳入 Any -> Any
倒淫、Any -> Int
伙菊、Int -> Int
三種類型的函數(shù)也都是允許的。
譯者注:上述這個 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 找我吧~
校者注:關于協(xié)變與逆變,還可以參考翻譯組翻譯的另外一篇文章血淌,解釋的更加詳細:Friday Q&A 2015-11-20:協(xié)變與逆變
[1]: 我并不太理解括號中的內(nèi)容矩欠。對于這段話想表達的意思,舉個例子來說明應該是悠夯,定義類型 Animal
及其子類型 Cat
癌淮,對于函數(shù) test(catAnimalF: Cat -> Animal)
中的函數(shù)類型 A catAnimalF: Cat -> Animal
來說,是可以使用函數(shù) B animalCatF: Animal -> Cat
來替換的沦补。因為 animalCatF
相較于 catAnimalF
乳蓄,其參數(shù)類型 Animal
是比 Cat
更加泛用的父類型,而其返回值類型則更加特殊夕膀。但作者在括號內(nèi)的解釋我卻沒看懂虚倒。第一個括號是想說,用 B 替代 A 之后产舞,相較于 A 所聲明的參數(shù)類型(Cat
)魂奥,A 的調(diào)用者(test
)能夠處理一個更加特殊的類型?感覺不對誒...第二個括號意思是易猫,在用 B 替代 A 后耻煤,其調(diào)用者(test
)會把返回的類型(Cat
)作為 A 中聲明的返回類型(Animal
)的簡化版處理?這個倒好像說的過去囧...(校者注:第一個括號的理解,A 的調(diào)用者哈蝇,也就是函數(shù) test
嘴办,函數(shù)類型的入?yún)⒖梢允?Cat
的父類,也就是 Animal
买鸽,譯者理解是對的涧郊,第二個括號理解也是對的。)(定稿注:正好翻譯了之前那篇「協(xié)變與逆變」眼五,所以譯者的理解是正確的妆艘。)
本文由 SwiftGG 翻譯組翻譯,已經(jīng)獲得作者翻譯授權看幼,最新文章請訪問 http://swift.gg批旺。