編程范式是程序語言背后的思想。代表了程序語言的設計者認為程序應該如何被構建和執(zhí)行。常見的編程范式有:過程式、面向對象、函數(shù)式、泛型編程等。
一些編程語言是專門為某種特定范式設計的,例如,C語言是過程式編程語言;Smalltalk和Java是較純粹的面向對象編程語言;Haskell、Scheme、Clojure是函數(shù)式編程語言。
另外一些編程語言和編程范式的關系并不一一對應,如Python、Scala、Groovy同時支持面向對象和一定程度上的函數(shù)式編程。Swift 也是支持多種編程范式的編程語言。
由于代表了語言背后的思想,編程范式很大程度上決定了語言會呈現(xiàn)為何種面貌。用不著深入學習,僅僅瀏覽代碼,就能發(fā)現(xiàn)Scala和Swift很類似,這是因為它們支持的編程范式是類似的;Scheme和Swift看起來就相差很遠,這是因為它們支持的編程范式很不一樣。對于理解一門編程語言而言,相對于語言的語法和編寫經(jīng)驗,理解語言的編程范式更重要。因為,就像看一本書,琢磨作者如何用詞,如何構建章節(jié)是很重要,但更重要的是理解書所要表達的思想。
Swift即支持面向對象編程范式,也支持函數(shù)式編程范式,同時還支持泛型編程。Swift支持多種編程范式是由它的目標決定的。Swift創(chuàng)造的初衷就是提供一門實用的工業(yè)語言。不同于Haskell這類出自大學和研究機構的具有學術性質的編程語言。蘋果推出Swift時就帶著著明確的商業(yè)目的:Mac OS和iOS系統(tǒng)的主要編程語言 Objective-C已顯老態(tài),Swift將使得蘋果系統(tǒng)的開發(fā)者擁有一門更現(xiàn)代的編程語言,從而促進蘋果整個生態(tài)圈的良性發(fā)展。
Swift的設計和開發(fā)無不體現(xiàn)著“實用的工業(yè)語言”這一目標。這決定了 Swift無法做極端的語言實驗,它需要在理智地面對現(xiàn)實的基礎上,謹慎地尋求突破。這就決定了Swift需要繼承歷史遺產(chǎn),在照顧現(xiàn)在大多數(shù)程序員的現(xiàn)實需求基礎上,面向未來有所發(fā)展。
面向對象編程的核心概念是繼承,多態(tài),和封裝。以對象構建程序的基本單元的面向對象編程語言中,繼承提供了一種復用代碼的方法;多態(tài)提供了更高的抽象能力,使得我們可以設計出更通用的程序;封裝提供一種使用代碼更為便捷安全的機制。Swift擁有以上所有的面向對象特性。所以,Swift是一門完備的面向對象編程語言。
Swift繼承了Objective-C面向對象方面的主要特性,提供以類為主的封裝和繼承機制。但給予了結構體(Struct)和枚舉(Enum)更豐富的面向對象特征,使它們可以用于封裝更為復雜的對象。另外,相對于 Objective-C,Swift是一門更為安全的語言。
在繼承上,Swift不同于C++可以繼承一個或者若干個類,而類似于 Objective-C和Java,只能單繼承。但Swift可以實現(xiàn)多個協(xié)議(Java 中對應的是接口Interface)。這在一定程度上彌補了沒有多繼承的局限,同時又避免了多繼承難以控制的缺陷。
除了實現(xiàn)協(xié)議,Swift還可以實現(xiàn)多個擴展(Extension)。擴展是一種向已有的類,枚舉或者結構體添加新功能的方法。擴展和Objective-C中的分類(Category)類似,但與Objective-C中的分類不同的是,Swift中的擴展沒有名字。
C++和Java等大部分面向對象編程語言主要以類(Class)作為實現(xiàn)面向對象的基本結構。Swift則賦予了結構體(Struct)和枚舉(Enum)更多的面向對象特征,使結構體和枚舉也能承擔部分數(shù)據(jù)封裝工作。在其他一些語言需要用類來解決的場景中,Swift可以使用結構體和枚舉類型,而且更為合適。例如,Swift的Array和Dictionary是用結構體實現(xiàn),而不是用類實現(xiàn)的,這不同于大多數(shù)編程語言。
Swift的結構體和枚舉可以像類一樣,完成下列事情:
在封裝這一點上,結構體和枚舉幾乎和類完全一致。不同的地方是,結構體和枚舉是不能繼承或者被繼承的。所以,這兩種數(shù)據(jù)類型也就沒有多態(tài)性。
總結一下,Swift中的類和其他面向對象編程語言的類一樣是面向對象語言的核心概念,具有面向對象的基本特征。Swift的結構體和枚舉擁有比其他面向對象編程語言更多的面向對象特性,可以封裝更復雜的對象。但不可繼承,也就沒有了多態(tài)性。
結構體,枚舉與類的另外一個區(qū)別是:結構體和枚舉是值類型,而類是引用類型。
值類型在賦值和作為函數(shù)參數(shù)被傳遞時,實際上是在進行復制,操作的是對象的拷貝。Swift中有大量值類型,包括Number,String,Array,Dictionary,Tuple,Struct和Enum等。
引用類型在賦值和作為函數(shù)參數(shù)被傳遞時,傳遞的是對象的引用,而并不是對象的拷貝。這些引用都指向同一個實例。對這些引用的操作,都將影響同一個實例。
在Swift中區(qū)分值類型和引用類型是為了將可變的對象和不可變的數(shù)據(jù)區(qū)分開來。可變的對象,使用引用類型;不可變的數(shù)據(jù),使用值類型。值類型的數(shù)據(jù),可以保證不會被意外修改。值類型的數(shù)據(jù)傳遞給函數(shù),函數(shù)內部可以自由拷貝,改變值,而不用擔心產(chǎn)生副作用。在多線程環(huán)境下,多個線程同時運行,可能會意外錯誤地修改數(shù)據(jù),這常常會是一種難以調試的bug。而使用值類型,你可以安全地在線程間傳遞數(shù)據(jù),因為值類型傳遞是拷貝,所以無需在線程間同步數(shù)據(jù)變化。這就可以保證代碼線程環(huán)境下的安全性。
結構體是值類型,暗示了結構體應該主要用于封裝數(shù)據(jù)。例如,三維坐標系中的點Point,代表幾何形狀的大小的Size等。而類是引用類型,意味著類應該用于封裝具有狀態(tài)的,可以繼承的對象。例如,人,動物等。
Swift中,Array、Dictionary、String都是值類型,它們的行為就像C語言中的Int一樣。你可以像使用Int一樣簡單安全地使用Array,而不用考慮深度拷貝之類煩人問題。Swift增強了對值類型的支持,鼓勵我們使用值類型。因為值類型更安全。更多地使用值類型,將有助于我們寫出行為更可預測,更安全的代碼。
Swift是強類型語言,這意味著Swift禁止錯誤類型的參數(shù)繼續(xù)運算。例如,你不能讓String和Float相加。這與C#和Java一致;而與C和Javascript這類弱類型語言不一樣。
Swift是靜態(tài)類型語言,這意味著Swift中變量是在編譯期進行類型檢查的。編譯時,編譯器會盡力找出包括類型錯誤在內的相關錯誤。例如,String和Int相加這種類型運算錯誤,編譯器在編譯時就能告訴你,而不會在運行時才報錯。這與C#和Java一致;而與Python和Ruby這類動態(tài)類型語言不一樣。
Swift不允許不正確的類型運算或類型轉換發(fā)生,所以Swift是類型安全的。
Swift支持類型推導,并且有一個相當不錯的類型推導器。大部分情況下,你都不用聲明類型,編譯器可以根據(jù)上下文為你推導出變量的類型。
Swift中類(包括結構體和枚舉)的初始化過程類似于Java的設計。Swift 有一類特別的方法,被作為初始化方法,它們沒有func前綴,而是以init為方法名。這不同于Objective-C中的初始化方法只是一個普通的方法。對于初始化方法的特殊處理可以在語言機制上保證初始化方法只被調用一次。這種機制在Objective-C中是不存在的,在 Objective-C中,初始化方法就像其它的普通方法一樣,可以被多次調用。
Swift中初始化方法必須保證所有實例變量都被初始化。Swift初始化方法要求特殊的初始化順序。先保證當前類的實例變量被初始化,再調用父類的初始化方法完成父類實例變量的初始化。
Swift保證了初始化方法只會被調用一次,同時所有的實例變量都會被初始化。這使得Swift初始化過程很安全。
Swift提供了重寫(Overriding)保護機制。如果要重寫基類的方法,就必須在子類的重寫方法前加上overriding關鍵字。這么做是向編譯器聲明你想提供一個重寫版本。編譯器會確認,基類里是否存在具有相同方法定義的方法。如果,基類中沒有相同的方法定義,編譯器就會報錯。另一方面,如果沒有加上overriding關鍵字的方法和基類的某個方法的定義相同,編譯器也會報錯,以防止意外的重寫行為。這樣就能從兩方面保證重寫行為的正確性。
Swift中的Optionals讓我們能夠更安全地應對有可能存在,也有可能不存在的值。在Objective-C里我們主要依靠文檔來了解一個API是否會返回nil。Optionals則讓我們將這份責任交給了類型系統(tǒng)。如果API的返回值聲明為Optional,就表示它可以是nil。如果它不是Optional,就表示它不可能是nil。
在Swift中,類型后面加問號聲明Optional類型,以及感嘆號!對 Optional類型拆包都只是語法糖。Optionals其實是由枚舉實現(xiàn)的:
enum Optional<T> : Reflectable, NilLiteralConvertible { case None case Some(T) //... }
也就是說,Optional其實是一種枚舉類型。我們通過語言的類型系統(tǒng)來明確可能為nil的情況。這比Objective-C中使用文檔來說明要安全得多。
現(xiàn)在絕大部分程序員的工作語言仍然是面向對象編程語言。大部分流行的現(xiàn)代編程語言都會允許你創(chuàng)建對象。面向對象編程語言易于建模。因為,對象和類似乎很容易和現(xiàn)實世界中的事物和概念對應。但編程實踐表明,任何東西都成為對象并不是一件好事情。舉一個Java中的蹩腳例子:Java中只有對象才能作為參數(shù)傳入函數(shù)(當然還有原始類型Primitive Type),所以為了將函數(shù)作為參數(shù)傳遞給另一個函數(shù),需要將函數(shù)包裹在一個對象中,通常會使用一個匿名類(這也是Java中,監(jiān)聽器 Listener通常的實現(xiàn)方法),而這個類不會有其他作用,只是為了滿足 Java一切皆為對象的設計,從而通過編譯。
Java擁有純粹的面向對象概念。它從設計之初,就希望以一切皆為對象的純對象模型來為世界建模。但發(fā)展到現(xiàn)在,Java中加入了越來越多非對象的東西。引入了閉包,從而獲得了函數(shù)式編程中的一級函數(shù);引入泛型,從而獲得了參數(shù)化的類型。這可能暗示了,這個世界是如此豐富多彩,使用單一模型為世界建模并不會成功。
Swift在追求統(tǒng)一純粹的編程范式這一點上并不固執(zhí)。Swift完整地支持面向對象編程,擁有完備的面向對象基礎概念。這使得熟悉面向對象編程的程序員學習和使用Swift的成本降低了。Java或者Objective-C程序員對Swift的很多概念會覺得很熟悉。對他們而言,學習Swift并不困難,很快就能將Swift投入到實際生產(chǎn)之中。
同時,Swift還一定程度上支持函數(shù)式編程風格。在適合函數(shù)式編程的場景下,同時程序員又擁有函數(shù)式編程的思維和能力時,可以使用Swift 以函數(shù)式的編程方法改善生產(chǎn)力。這將在下一章詳細介紹。
函數(shù)式編程是一種以數(shù)學函數(shù)為程序語言建模的核心的編程范式。它將計算機運算視為數(shù)學函數(shù)計算,并且避免使用程序狀態(tài)以及可變對象。函數(shù)式編程思想主要有兩點:
函數(shù)是函數(shù)式編程的基石。函數(shù)式編程語言的代碼就是由一個個函數(shù)組合而成的。編寫函數(shù)式語言的過程就是設計函數(shù)的過程。大規(guī)模程序由成千上萬的函數(shù)組成,為了有效的組合這些函數(shù)。函數(shù)式編程語言,會盡量避免狀態(tài),避免可變對象。沒有可變的狀態(tài),就使得函數(shù)式語言中的函數(shù)變?yōu)榱思兒瘮?shù)。純函數(shù)更容易模塊化,更容易理解,對于復用是友好的。
函數(shù)式編程的核心是函數(shù),函數(shù)是“頭等公民”。這就像面向對象語言的主要抽象方法是類,函數(shù)式編程語言中的主要抽象方法是函數(shù)。Swift中的函數(shù)具有函數(shù)式語言中的函數(shù)的所有特點。你可以很容易地使用Swift 寫出函數(shù)式風格的代碼。
高階函數(shù),指可以將其他函數(shù)作為參數(shù)或者返回結果的函數(shù)。
一級函數(shù),進一步擴展了函數(shù)的使用范圍,使得函數(shù)成為語言中的“頭等公民”。這意味函數(shù)可在任何其他語言構件(比如變量)出現(xiàn)的地方出現(xiàn)。可以說,一級函數(shù)是更嚴格的高階函數(shù)。
Swift中的函數(shù)都是一級函數(shù),當然也都是高階函數(shù)。
前文中舉過Java中為了將函數(shù)作為參數(shù)傳遞給另外一個函數(shù),需要將函數(shù)包裹在一個多余的匿名類中的蹩腳例子。Swift函數(shù)都是一級函數(shù),可以直接將函數(shù)作為參數(shù)傳遞給另外一個函數(shù)。這就避免了Java里出現(xiàn)的這種多余的匿名類。
閉包是一個會對它內部引用的所有變量進行隱式綁定的函數(shù)。也可以說,閉包是由函數(shù)和與其相關的引用環(huán)境組合而成的實體。?函數(shù)實際上是一種特殊的閉包。
Objective-C在后期加入了對閉包支持。閉包是一種一級函數(shù)。通過支持閉包,Objective-C拓展其語言表達能力。但是如果與Swift的閉包語法相比,Objective-C的閉包會顯得有些繁重復雜。 以下示例顯示了Swift閉包語言的簡潔和優(yōu)雅:
let r = 1...3 let t = r.map { (i: Int) -> Int in return i * 2 }
該例中,map函數(shù)遍歷了數(shù)組,用作為函數(shù)參數(shù)被傳入的閉包處理了數(shù)組里的所有元素,并返回了一個處理過的新數(shù)組。例子中可以看到,Swift 中使用{}來創(chuàng)建一個匿名閉包。使用in來分割參數(shù)和返回類型。在很多情況下,由于存在類型推導,可以省略類型聲明。
在介紹Swift的不變性之前,先討論一下Haskell這門純函數(shù)式語言。這將有助于我們對于不變性有更深刻的理解。
簡單而言,Haskell 沒有變量。這是因為,Haskell追求更高級別的抽象,而變量其實是對一類低級計算機硬件:存儲器空間(寄存器,內存)的抽象。變量存在的原因,可以視為計算機語言進化的遺跡,比如在初期直接操作硬件的匯編語言中,需要變量來操作存儲過程。而在計算機出現(xiàn)之前,解決數(shù)學計算問題都是圍繞構建數(shù)學函數(shù)。數(shù)學中,不存在計算機語言中這種需要重復賦值的變量。
Haskell基于更抽象的數(shù)學模型。使用Haskell編程只需專注于設計數(shù)據(jù)之間的映射關系。而在數(shù)學上,表示兩個數(shù)據(jù)之間映射關系的實體就是函數(shù)。這使得編寫Haskell代碼和設計數(shù)學函數(shù)的過程是一致的,Haskell 程序員的思路也更接近數(shù)學的本質。Haskell摒棄了變量的同時,也拋棄了循環(huán)控制。這是因為沒有變量,也就沒有了控制循環(huán)位置的循環(huán)變量。這也很好理解?;貞浺幌挛覀冊趯W習計算機之前的數(shù)學課程中,也無需使用到for這類概念。我們還是使用函數(shù)處理一個序列到另外一個序列的轉換。
不變性導致另外一個結果,就是純函數(shù)。沒有可變的狀態(tài),沒有可變對象,就使得函數(shù)式語言中的函數(shù)變?yōu)榱思兒瘮?shù)。純函數(shù)即沒有副作用的函數(shù),無論多少次執(zhí)行,相同的輸入就意味著相同的輸出。一個純函數(shù)的行為并不取決于全局變量、數(shù)據(jù)庫的內容或者網(wǎng)絡連接狀態(tài)。純代碼天然就是模塊化的:每個函數(shù)都是自包容的,并且都帶有定義良好的接口。純函數(shù)具有非常好的特性。它意味著理解起來更簡單,更容易組合,測試起來更方便,線程安全性。
Swift提供了一定程度的不變性支持。在Swift中,可以使用var聲明普通的變量,也可以使用let快捷方便地聲明不變量。
//變量 var mutable //不變量 let immutable = 1
Swift 區(qū)分var和let是為了使用編譯器來強制這種區(qū)分。Swift 中聲明了不變量,就必須在聲明時同時初始化,或者在構造器中初始化。除這兩個地方之外,都無法再改變不變量。Swift中鼓勵使用不變量。因為,使用不變量更容易寫出容易理解,容易測試,松耦合的代碼。
不變性有諸多好處。
更高層次的抽象。程序員可以以更接近數(shù)學的方式思考問題。
更容易理解的代碼。由于不存在副作用,無論多少次執(zhí)行,相同的輸入就意味著相同的輸出。純函數(shù)比有可變狀態(tài)的函數(shù)和對象理解起來要容易簡單得多。你無需再擔心對象的某個狀態(tài)的改變,會對它的某個行為(函數(shù))產(chǎn)生影響。
線程安全的代碼。這意味著多線程環(huán)境下,運行代碼沒有同步問題。它們也不可能因為異常的發(fā)生而處于無法預測的狀態(tài)中。
不像Haskell這種純函數(shù)式編程語言只能申明不可變量,Swift提供變量和不可變量兩種申明方式。程序員可以自由選擇:在使用面向對象編程范式時,可以使用變量。在需要的情況下,Swift也提供不變性的支持。
惰性計算是函數(shù)式編程語言的一個特性。惰性計算的表達式不在它被綁定到變量之后就立即求值,而是在該值被取用的時候求值。惰性計算有如下優(yōu)點。
純函數(shù)式編程語言,如Haskell中是默認進行惰性求值的。所以,Haskell被稱為惰性語言。而大多數(shù)編程語言如Java、C++求值都是嚴格的,或者說是及早求值。Swift默認是嚴格求值,也就是每一個表達式都需要求值,而不論這個表達式在實際中是否確實需要求值。但是,Swift 也提供了支持惰性求值的語法。在需要惰性時,需要顯式聲明。這為開發(fā)者在Swift中使用惰性提供了條件。
下面的例子展示了將默認是嚴格求值的數(shù)組變?yōu)槎栊孕蛄校?
let r = 1...3 let seq = lazy(r).map { (i: Int) -> Int in println("mapping \(i)") return i * 2 } for i in seq { println(i) }將獲得如下結果:
mapping 1 2 mapping 2 4 mapping 3 6
結果顯示seq是一個惰性序列。它的值只有在需要時才會真正發(fā)生計算。
函數(shù)式編程語言并不年輕,它的歷史和面向對象編程一樣悠久。1958年被創(chuàng)造出來的Lisp是最古老的函數(shù)式編程語言。它比C語言年代更為久遠。但直到最近,函數(shù)式編程思想才逐漸被重視。幾乎所有新發(fā)明的編程語言都或多或少受到了函數(shù)式編程思想的影響。Python、Scala、Groovy、Swift都有一級函數(shù),閉包。使得你可以將函數(shù)直接傳給另外一個函數(shù),函數(shù)也能夠以返回值形式被另一個函數(shù)返回。消除狀態(tài),提供不變性的好處越來越多被接受,Scala、Groovy、Swift都提供了聲明不可變對象的方法,以支持編寫更趨近于函數(shù)式風格的代碼。
函數(shù)編程語言有其優(yōu)秀的地方,也許將來會成為一個重要的編程范式。但是,函數(shù)式編程的重要性可能更多會間接地體現(xiàn)在影響其他編程語言的發(fā)展上。未來,可能很難出現(xiàn)一門主要以函數(shù)式編程范式設計的主流編程語言。如同Java這樣的以單一編程范式(面向對象)構建,而成為主流的編程語言的機會應該不會太多了。如同Haskell這樣追求純粹的函數(shù)式編程語言,更多的可能只是一個偏學術的語言實驗。
容我再重復一次上一節(jié)提到的理由:這個世界是如此豐富多彩,使用單一模式為世界建??赡懿⒉粫晒?。當然,這類預測常常會被打破。如果,將來計算機領域出現(xiàn)了能解決所有問題的統(tǒng)一范式,我將很樂意再次學習和討論它。但如果僅僅討論現(xiàn)狀的話,我們仍然不得不面對一個分裂和折衷的世界。
Swift并不是一門主要以函數(shù)式編程范式構建的語言,它更多的是借鑒融合了函數(shù)式編程一些優(yōu)秀思想(更靈活強大的函數(shù),不變性的優(yōu)點)。Swift在大多數(shù)的場景下,仍然主要會以面向對象編程語言的面目出現(xiàn)。因為,作為另一門面向對象編程語言Objective-C的繼任者,Swift 需要繼承Objective-C的遺產(chǎn):Cocoa。我們現(xiàn)在寫Swift代碼,大部分時候還是在Cocoa框架之上,可以說 Cocoa就是Swift的標準庫。在一個主要以面向對象語言編寫的框架中寫代碼,最合適的思維方式仍然會是面向對象的。Cocoa可以說是Swift得以在高起點出發(fā)的基礎,也可以說其發(fā)生胎換骨變化的阻礙。
Swift對函數(shù)式編程的支持,使得程序員多了一種選擇。Swift并不強迫程序員一定要以面向對象的方法思維。在場景合適的情況下,程序員可以選擇使用函數(shù)式風格編寫代碼。如果確實是合適的場景,就能夠改善生產(chǎn)力。
如果,我們按語言范式給現(xiàn)在流行的語言分類,支持面向對象的編程語言應該會是最長的隊伍?,F(xiàn)在大部分流行的現(xiàn)代編程語言都是面向對象的,它們都會允許你創(chuàng)建對象。但同時,你會發(fā)現(xiàn)比較流行的幾個編程語言,Python、Scala甚至Java都或多或少都受到了函數(shù)式編程語言的影響。它們都引入一些函數(shù)式編程的概念,可以在一定程度上編寫出具有函數(shù)式風格的代碼。
在熟悉了類面向對象編程語言之后,再接觸函數(shù)式編程語言,常常會覺得耳目一新,甚至隱約覺得函數(shù)式語言會是救世良方。那我們是否應該就此徹底轉向函數(shù)式編程語言呢?使用Haskell來拯救世界?
面向對象編程語言在大規(guī)模實踐之后,我們確實更深刻地了解了它們的缺點(例如,難以編寫多線程環(huán)境下的軟件應用;繼承并不是代碼復用的好方法)。函數(shù)式語言也確實有不少優(yōu)點,有些優(yōu)點恰恰就能解決面向對象語言的問題(純函數(shù)十分適應多線程環(huán)境,純函數(shù)天生就是模塊化的,對于代碼復用十分友好)。但是,函數(shù)式編程也許也存在某些問題。而這些問題,可能要在更大規(guī)模的業(yè)界實踐之后才會暴露出來。現(xiàn)在我們已經(jīng)認識到,單純以對象為世界建模是有困難的。那么以數(shù)學模型來為世界建模可能也并不會好到哪里去。而可以確信的是,它們都有自己各自擅長的領域和環(huán)境。我們仍然還無法使用某種單一的編程范式來解決所有問題。
更大的現(xiàn)實是無數(shù)企業(yè)已經(jīng)在面向對象編程語言上做了巨大的投資,即使現(xiàn)在面向對象編程已經(jīng)暴露出一些問題,而函數(shù)式編程又呈現(xiàn)出不少能解決這些問題的優(yōu)點,任何一個謹慎的人都不會,也不可能馬上拋棄面向對象編程,徹底全面地轉向函數(shù)式編程語言。
現(xiàn)實的選擇是支持面向對象編程的同時,提供函數(shù)式的支持。這樣,在大部分面向對象游刃有余的地方,仍然可以使用面向對象的方法。而在適合函數(shù)式編程的地方,而你又擁有函數(shù)式編程的思維和能力時,還可以采用函數(shù)式的編程方法改善生產(chǎn)力。
Swift就是這樣一個現(xiàn)實的選擇。完善的面向對象支持,使Swift繼承了 Objective-C遺留下來的豐厚遺產(chǎn)。在Swift中使用Objective-C對象并不復雜。如果,你遇到一個對多線程安全性有要求的場景,需要使用函數(shù)式風格編寫這部分代碼,這在Swift中也是很輕松的。
泛型編程是另外一個有趣的話題。泛型為程語言提供了更高層級的抽象,即參數(shù)化類型。換句話說,就是把一個原本特定于某個類型的算法或類當中的類型信息抽象出來。這個抽象出來的概念在C++的 STL(Standard Template Library)中就是模版(Template)。STL 展示了泛型編程的強大之處,一出現(xiàn)就成為了C++的強大武器。除C++之外,C#、Java、Haskell等編程語言也都引入了泛型概念。
泛型編程是一個稍微局部一些的概念,它僅僅涉及如何更抽象地處理類型。這并不足以支撐起一門語言的核心概念。我們不會聽到一個編程語言是純泛型編程的,而沒有其他編程范式。但正因為泛型并不會改變程序語言的核心,所以在大多數(shù)時候,它可以很好地融入到其他編程范式中。C++、Scala、Haskell這些風格迥異的編程語言都支持泛型。泛型編程提供了更高的抽象層次,這意味著更強的表達能力。這對大部分編程語言來說都是一道美味佐餐美酒。
在Swift中,泛型得到廣泛使用,許多Swift標準庫是通過泛型代碼構建出來的。例如Swift的數(shù)組和字典類型都是泛型集合。這樣的例子在 Swift中隨處可見。
Swift函數(shù)支持泛型。泛型函數(shù)通過將函數(shù)參數(shù)和返回值定義為泛型類型,使得函數(shù)可以作用于任何適合的類型。下面展示了一個簡單的泛型函數(shù):
func swapTwoValues<T>(inout a: T, inout b: T) { let temporaryA = a a = b b = temporaryA }
除了泛型函數(shù)之外,Swift還可以自定義泛型類,泛型結構體和泛型枚舉。這樣的泛型類型可以作用于任何類型,其用法和Swift提供的 Array和Dictionary相同。
用一個棧(Stack)的例子展示泛型結構體的定義和使用。泛型枚舉和泛型類的定義和使用方法是相同的。
// 定義一個泛型結構體 struct Stack<T> { var items = [T]() mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } } // 使用一個泛型結構體 var stackOfStrings = Stack<String>() stackOfStrings.push("uno")
泛型類型參數(shù)T被用在了三個地方:
而對于協(xié)議,Swift中沒有提供類似結構體或類那樣的方法來定義泛型協(xié)議。但我們可以使用typealias關鍵字定義該協(xié)議的關聯(lián)類型,這樣一定程度上可以模擬泛型協(xié)議的效果,例子如下:
protocol GeneratorType { typealias Element mutating func next() -> Element? }
實現(xiàn)該協(xié)議的類必須定義一個別名為Element的關聯(lián)類型。這和泛型的概念異曲同工,一定程度上實現(xiàn)了泛型協(xié)議。
在泛型的編程實踐中,我們會遇到一些需要對泛型類型做進一步約束的場景。類型約束為泛型參數(shù)指定了一個類型,或者要求其實現(xiàn)某個特定的協(xié)議。比如,`意味著泛型參數(shù)指代的對象需要遵守 Equatable協(xié)議。
類型約束對泛型參數(shù)的類型做了一定約束,可以強制要求泛型參數(shù)代表的類型遵守某個協(xié)議。而where語句可以更進一步對類型約束中聲明的泛型參數(shù)所需要遵守的協(xié)議作出更詳細的要求。where語句也可以對協(xié)議的關聯(lián)類型作進一步約束。比如,你可以要求兩個泛型參數(shù)所遵守的協(xié)議的關聯(lián)類型是相同的。
總體而言,Swift提供了全面的泛型編程語法,讓程序員可以寫出抽象層次更高,更為靈活的代碼,在避免了重復代碼的同時,又能擁有良好的類型安全性。
最后總結一下,Swift是一門典型的多范式編程語言,支持面向對象是為了繼承面向對象編程豐厚的成果;支持函數(shù)式編程,是為了探索新的可能;支持泛型編程,則是一道美味的佐餐美酒。
Swift允許程序員在大部分使用面向對象就游刃有余的時候,輕松地繼續(xù)使用面向對象編程;而在適合函數(shù)式編程的場景下,同時程序員又擁有函數(shù)式編程的思維和能力時,還可以使用Swift以函數(shù)式的編程方法改善生產(chǎn)力;以及任何時候程序員都可以在Swift中使用泛型,提高抽象層次。