讀古今文學網 > Java程序員修煉之道 > 7.2 語言生態學 >

7.2 語言生態學

編程語言有多種不同的流派和類型。也就是說,不同的語言有不同的編碼風格和編碼方式。如果想掌握這些風格並讓它們為你所用,你得弄明白這些差異以及如何給語言分類。

注意 這些分類可以幫助思考語言的多樣性。儘管某些分法可能更清晰,但沒有哪種是完美的。

最近幾年,語言在添加新特性時有跨越各種分類的趨勢。這就是說,你最好認為某種語言比其他語言「函數化程度更低」,或者「雖然是動態類型,但必要時也有可選的靜態類型」。

我們將要討論的分類是「解釋型與編譯型」、「動態與靜態」、「命令式與函數式」,還有在JVM上重新實現的語言與原生語言。通常這些分類用來明確語言的邊界,不要用學院化方式把它們當做完整精確的分類。

Java是運行時編譯、靜態類型的命令式語言。它強調安全性、代碼清晰、性能,並樂於表現出一定程度的繁瑣和死板(比如在部署中)。不同的語言可能側重不同,比如動態類型的語言可能更看重部署速度。

我們先從解釋型與編譯型分類開始介紹。

7.2.1 解釋型與編譯型語言

解釋型語言是那種源碼是什麼就執行什麼的語言,不會在執行開始之前把整個程序轉成機器碼。編譯型語言則不同,一開始就要用編譯器把人類可讀的源碼變成二進制形式。

這種分別最近變模糊了。80年代和90年代早期這兩類語言的邊界還相當清晰:C/C++及類似的語言是編譯型,Perl和Python是解釋型。但Java同時兼具編譯型和解釋型兩種特性,這一點我們已經在第1章講過了。字節碼的出現使這個界限更模糊了。人類肯定讀不了字節碼,但它也不是真正的機器碼。

對於本書中要研究的JVM語言,我們劃分的邊界是該語言是否會將源碼編譯為類文件並且執行。不產生類文件的語言會由解釋器(可能是用Java寫的)逐行執行源碼。有些語言既有編譯器也有解釋器,還有些既有解釋器又有產生JVM字節碼的即時編譯器(JIT)。

7.2.2 動態與靜態類型

在動態類型語言中,變量在不同時間可能會有不同的類型。我們以一小段簡單的JavaScript代碼為例,JavaScript是著名的動態語言。即便你不瞭解這種語言,也應該很容易理解下面的代碼:

var answer = 40;
answer = answer + 2;
answer = \"What is the answer? \" + answer;
  

在這段代碼中,變量answer一開始被賦值為40,當然,是個數值。然後給它加上2,變成了42。之後我們給answer賦了個字符串值。這在動態語言中是非常普遍的技術,不會引起語法錯誤。

JavaScript解釋器也能分清兩種+操作符的用法。第一個+是數字相加——把2加到40上,而在下一行中,解釋器能從上下文中推導出開發人員要做字符串合併。

注意 這裡的關鍵是動態類型語言跟蹤變量值的類型(比如數字或字符串)信息,而靜態類型語言跟蹤變量的類型信息。

靜態類型非常適合編譯型語言,因為所有類型信息都在變量上,跟變量的值沒有關係。這樣很容易在編譯時推導潛在的類型系統違規行為。

動態類型語言把類型信息放在變量所持有的值上。也就是說很難提前發現類型違規行為,因為推導所需的信息直到運行時才能得到。

7.2.3 命令式與函數式語言

Java 7是典型的命令式語言。命令式語言把程序的運行狀態建模為可修改的數據,用一系列指令來改變運行狀態。因此,在命令式語言中,程序狀態才是核心概念。

命令式語言主要分為兩類。一種是過程語言,比如BASIC和FORTRAN。這種語言把代碼和數據完全分開,有簡單的代碼操作數據範式。另外一種是面向對像(OO)語言,數據和代碼(以方法的形式)共同封裝在對像中。面向對像語言中或多或少地存在元數據(比如類信息)引入的額外結構。

函數式語言不同,它把計算本身當做最重要的概念。函數式語言跟過程語言一樣對值進行操作,但它不會修改輸入,而是像數學函數一樣返回新值。

如圖7-2所示,函數被看做「小處理機」,輸入值並輸出新值。它們沒有任何自己的狀態,並且把它們和任何外部狀態綁在一起也沒有任何意義。這就是說一切皆對象的世界觀跟函數式語言的自然觀點有些分歧。

圖7-2 命令式和函數式語言

在接下來的三章裡,每章重點介紹一種語言,並且都會以前面對函數式編程的討論為基礎。我們會從Groovy開始,它帶「一點兒函數式風格」,用我們在7.1節討論過的方式處理集合;然後是Scala,對FP的利用更加充分;最後是Clojure(純粹的函數式語言,完全沒有面向對像特性)。

7.2.4 重新實現的語言與原生語言

JVM語言之間的另一個重要區別是重新實現已有語言與專門以JVM為目標的劃分。通常來說,那些專門以JVM為目標寫的語言能把自己的類型系統跟JVM的原生類型結合得更緊密。

下面是三種重新實現已有語言的JVM語言。

  • JRuby在JVM上重新實現了Ruby語言。Ruby是一個動態類型的面向對像語言,有些函數式特性。它在JVM上基本算解釋型的,但最近發佈的版本中有一個運行時JIT編譯器,在適當條件下可以生成JVM字節碼。

  • Jython由Jim Hugunin在1997年發起,當時是為了在Python中使用高性能的Java類庫。它在JVM上重新實現了Python,因此是動態的,總體還算面向對像語言。它的運作方式是先在內部生成Python字節碼,然後再轉換成JVM字節碼。這使它能在Python典型的解釋模式(看起來像)下工作。通過生成JVM字節碼,並把結果類文件保存到硬盤上,它也能在預先(AOT)編譯模式下工作。

  • Rhino最初是由Netscape開發的,後來轉給Mozilla項目。它在JVM上提供了一個JavaScript實現。JavaScript是動態類型的面向對像語言(但和Java實現面向對象的方式截然不同)。Rhino既支持編譯模式也支持解釋模式,隨Java 7一起發佈(具體細節請參見com.sun.script.javascript包)。

最早的JVM語言

很難確定最早的JVM語言(除Java之外)是什麼。可以肯定的是在1997年前後就出現了Kawa語言,它是一種Lisp語言。在那之後這些語言幾乎呈現了爆炸式增長,因此追蹤它們的歷史太難了。

在編寫本書時,猜測至少有200種JVM語言應該是合理的。不能說所有語言都很活躍或得到了廣泛應用,但這個數字起碼能表明JVM是一個非常活躍的語言開發和實現平台。

注意 在隨Java 7推出的語言和VM規範裡,所有對Java語言的直接引用都從VM規範中去掉了。Java現在只是運行在JVM上的眾多語言中的普通一員,它不再享有特權了。

就像我們在第5章中討論的,能讓這麼多不同的語言運行在JVM上的關鍵技術是類文件格式。任何能產生類文件的語言都可以認為是JVM上的編譯型語言。

我們接下來會討論多語言編程怎麼變成了讓Java程序員感興趣的領域。我們會解釋基本概念,為什麼要給我們的項目選擇一種備選的JVM語言以及如何操作。