Swift 2.1 函數(shù)類型轉(zhuǎn)換:協(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)的語境中芒填,復雜類型的兼容問題可以歸結(jié)為一個簡單的問題:當你需要使用 A 類型的函數(shù)時,在什么情況下使用 B 類型的函數(shù)進行替代是安全的嫩舟?

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

譯者注:如果 T1T2 的子類型蜀踏,則可以表示為 T1 < T2,那么上面的規(guī)則就可以表示為:對函數(shù)類型 F1 = S1 -> T1F2 = S2 -> T2 來說掰吕,當且僅當 S2 < S1T1 < T2 時果覆,F1F2 的子類型。
S1S2 在入?yún)⑽恢蒙现呈欤麄冎g的關系和 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' 轉(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批旺。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市诵姜,隨后出現(xiàn)的幾起案子汽煮,更是在濱河造成了極大的恐慌,老刑警劉巖棚唆,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件暇赤,死亡現(xiàn)場離奇詭異,居然都是意外死亡宵凌,警方通過查閱死者的電腦和手機鞋囊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瞎惫,“玉大人溜腐,你說我怎么就攤上這事」侠” “怎么了挺益?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長乘寒。 經(jīng)常有香客問我望众,道長,這世上最難降的妖魔是什么肃续? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任黍檩,我火速辦了婚禮,結(jié)果婚禮上始锚,老公的妹妹穿的比我還像新娘刽酱。我一直安慰自己,他們只是感情好瞧捌,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布棵里。 她就那樣靜靜地躺著润文,像睡著了一般。 火紅的嫁衣襯著肌膚如雪殿怜。 梳的紋絲不亂的頭發(fā)上典蝌,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機與錄音头谜,去河邊找鬼骏掀。 笑死,一個胖子當著我的面吹牛柱告,可吹牛的內(nèi)容都是我干的截驮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼际度,長吁一口氣:“原來是場噩夢啊……” “哼葵袭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起乖菱,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤坡锡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后窒所,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鹉勒,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年墩新,在試婚紗的時候發(fā)現(xiàn)自己被綠了贸弥。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡海渊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出哲鸳,到底是詐尸還是另有隱情臣疑,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布徙菠,位于F島的核電站讯沈,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏婿奔。R本人自食惡果不足惜缺狠,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望萍摊。 院中可真熱鬧挤茄,春花似錦、人聲如沸冰木。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至歇终,卻和暖如春社证,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背评凝。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工追葡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奕短。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓宜肉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親篡诽。 傳聞我的和親對象是個殘疾皇子崖飘,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容