官术网_书友最值得收藏!

第1章 引言

1.1 編程語言的“紋理”

像木材一樣,編程語言也有“紋理”——無論是在做木工還是在編程的過程中,當你順著“紋理”工作時,事情就會很順利;當你不按“紋理”工作時,事情就會變得更難。如果你不按編程語言的“紋理”工作,就必須寫更多的代碼,代碼性能也會受到影響,更容易引入缺陷,通常必須覆蓋預置的默認值,并且每進行一步都需要與工具進行“戰斗”。

不按“紋理”工作,就需要不斷努力,且不一定有回報。

例如,使用函數式的方式編寫Java代碼一直以來都是可行的,但在Java 8之前很少有程序員這樣做。

下面是一段Kotlin的代碼,它通過使用加法運算符對列表進行折疊,以計算列表中數字的和:

將上述代碼與實現相同功能的Java 1.0代碼進行比較。那是1995年,函數不是Java 1.0的“頭等公民”,所以我們必須把函數實現為對象,并且為不同類型的函數定義自己的接口。例如,加法函數需要兩個參數,所以我們必須定義一個雙參函數:

然后,我們必須編寫fold高階函數,隱藏Vector類所需的迭代和變換過程(1995年的Java標準庫尚未包含Collections Framework)。

我們必須為每一個想要傳遞給fold(高階)函數的函數單獨定義一個類,加法運算符不能作為值傳遞,而且當時的Java語言并沒有方法引用、lambda或閉包,甚至沒有內部類。Java 1.0也沒有泛型或自動裝箱功能——我們必須將參數強制轉換為預期的類型,并編寫引用類型和原語之間的裝箱代碼:

最后,我們可以用這些代碼來計算列表的和:

在2020年主流語言中,這只是一個表達式,但需要付出很大的工作量。

但這還沒有結束,因為Java沒有標準的函數類型,我們還不能輕松地將不同的函數庫結合起來。我們必須編寫適配器類來映射不同庫中定義的函數類型。而且,由于虛擬機沒有JIT,只有一個簡單的垃圾收集器,函數式代碼的性能要比命令式代碼的性能差。

1995年,沒有足夠的好處來證明用函數式風格編寫Java代碼是正確的,Java程序員發現編寫迭代集合和修改狀態的命令式代碼更容易。編寫函數式代碼有悖于Java 1.0的“紋理”。

一種語言的“紋理”是隨著時間的推移而形成的,因為它的設計者和使用者建立了對語言特性如何相互作用的一致理解,并將他們的理解和偏好植入其他人所依賴的庫中。語言的“紋理”影響著程序員使用它編寫代碼的方式,而后者又影響著語言及其庫和編程工具的演變,從而改變了“紋理”,改變了程序員使用該語言編寫代碼的方式,不斷地進行著相互反饋和演變的循環。

例如,隨著時間的推移,Java 1.1中加入了匿名內部類,而Java 2則向標準庫中加入了Collections Framework。匿名內部類意味著我們不再需要為傳遞給fold函數的每個函數單獨命名一個類,但由此產生的代碼可能更加難以閱讀:

函數式編程習慣仍然與Java 2的“紋理”背道而馳。

快進到2004年,Java 5是Java的下一個重大升級版本。它增加了泛型和自動裝箱,這提高了類型安全性并減少了樣板代碼:

Java開發者經常使用谷歌的Guava庫(https://oreil.ly/dMX73)來為集合添加一些常見的高階函數(雖然fold不在其中),但即便是Guava的作者也建議首選編寫命令式代碼,因為它有更好的性能,通常也更容易閱讀。

函數式編程在很大程度上仍然與Java 5的“紋理”相悖,但我們可以看到向這個方向演進的趨勢。

Java 8中增加了匿名函數(又稱為lambda表達式)和方法引用,并在標準庫中增加了Streams API。編譯器和虛擬機優化了lambda,以避免匿名內部類的性能開銷。Streams API完全接納了函數式的風格,最終允許:

然而,這一切并非一帆風順。我們仍然不能把加法運算符作為參數傳遞給Streams的reduce函數,但標準庫函數Integer::sum可以做同樣的事情。由于引用類型和原始類型之間的區別,Java的類型系統仍然會產生棘手的邊緣場景。Streams API缺少一些我們所期望的、常見于函數式編程語言(例如Ruby)中的高階函數。受檢異常(checked exception)與Streams API和一般的函數式編程并不兼容。在編寫具有值語義的不可變類時,仍然需要大量的樣板代碼。但是在Java 8中,已經做出了根本上的改變,使得函數式風格編程得以運行,即便與語言的“紋理”并不完全一致,至少也不會違背它。

Java 8之后的版本增加了各種較小的語言特性和庫特性,支持更多的函數式編程風格,但沒有改變求和的計算方式。

就Java而言,語言的“紋理”以及程序員適應它的方式,經歷了幾種不同的編程風格的演變。

主站蜘蛛池模板: 石林| 恩平市| 秦皇岛市| 孝义市| 临高县| 兴隆县| 新野县| 桓仁| 海伦市| 丰城市| 化州市| 信阳市| 合作市| 喀什市| 宁国市| 西昌市| 沾益县| 宾川县| 乌什县| 河东区| 泽州县| 永年县| 疏勒县| 卢龙县| 碌曲县| 兴国县| 静海县| 安康市| 永福县| 西华县| 西青区| 荣昌县| 伊春市| 成安县| 扶绥县| 民县| 建昌县| 安宁市| 汉中市| 虞城县| 普格县|