Swift 2.1 函數(shù)類型轉換:協(xié)變與逆變

作者: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> 和兩個簡單類型: CarCar 的一個子類型(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)的語境中,復雜類型的兼容問題可以歸結為一個簡單的問題:當你需要使用 A 類型的函數(shù)時,在什么情況下使用 B 類型的函數(shù)進行替代是安全的?

一個通用的規(guī)則是,能夠兼容的函數(shù)類型有這樣的特征:其參數(shù)是更加泛用的父類型(相比于 A 函數(shù)所聲明的參數(shù)類型,A 的調(diào)用者也能夠處理更特殊的參數(shù)),返回的結果則是一個更加特殊的子類型(A 的調(diào)用者會把返回值的類型當成 A 中聲明的父類型的簡化版)[^1],參數(shù)是逆變的,而返回值是協(xié)變的。

譯者注:如果 T1T2 的子類型,則可以表示為 T1 < T2,那么上面的規(guī)則就可以表示為:對函數(shù)類型 F1 = S1 -> T1F2 = S2 -> T2 來說,當且僅當 S2 < S1T1 < T2 時,F1F2 的子類型。
S1S2 在入?yún)⑽恢蒙?,他們之間的關系和 F1F2 間的關系是相反的,所以入?yún)⑹悄孀兊模瑫r,T1T2 在出參位置上,他們之間的關系和 F1F2 間的關系是相同的,所以出參是協(xié)變的。

在 Swift 2.1 前的版本中,函數(shù)類型都是不變(invariance)的,如果你在 Playground 中嘗試運行下面的代碼,你會得到一些類似這樣的警告:

// Cannot convert value of type '(Int) -> Int' to expected argument type '(Int) -> Any 
// (無法把 '(Int) -> Int' 轉換為期望的參數(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ù)類型轉換,現(xiàn)在參數(shù)是逆變的,而返回值是協(xié)變的。

回到上面的示例代碼,即便 testVariance 函數(shù)輸入?yún)?shù)的類型是 Int -> Any,但現(xiàn)在傳入 Any -> Any、Any -> IntInt -> 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。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容