程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 面向Java開發人員的Scala指南 - Scala控制結構內部揭密

面向Java開發人員的Scala指南 - Scala控制結構內部揭密

編輯:關於JAVA

Scala 是專為 Java™ 平台編寫的,因此其語法設計會使 Java 代碼編碼人員感覺很輕松。同時,Scala 為 JVM 提供了函數語言的固有的強大功能,並以這些函數設計概念為出發點。在這一期的 面向 Java 開發人員的 Scala 指南系列 文章中,Ted Neward 將介紹兩種語言之間的細微差異,從一些控制結構(比如 if、while 和 for)開始介紹。正如您將要學習到的那樣,Scala 為這些結構提供了一些在其 Java 等效物中無法獲得的功能和復雜性。

迄今為止,在此 系列 中,我們已經討論了 Scala 對生態環境的保真度,展示了 Scala 如何將眾多的 Java 核心對象功能合並在一起。如果 Scala 只是編寫對象的另一種方式,那麼它不會有任何引人注意的地方,或者說不再那麼功能強大。Scala 的函數概念和對象概念的合並,以及它對編程人員效率的重視,這些使得學習 Scala 語言比 Java-cum-Scala 編程人員所想象的體驗更加復雜、更加微妙。

例如,對控制結構(比如 if、while 和 for)使用 Scala 的方法。盡管這些控制結構看起來類似一些老的、還比較不錯的 Java 結構,但實際上 Scala 為它們增加了一些完全不同的特性。本月的文章是關於使用 Scala 控制結構時能夠期望獲得哪些東西的入門級讀物,而不是在制造許多錯誤(並編寫一堆錯誤代碼)之後,讓您冒著遭受挫折的風險去尋找差異。

修訂後的 Person.scala

在 本系列的上一篇文章 中,可以了解到 Scala 能夠通過定義一些方法來定義 POJO,這些方法模仿基於 POJO 的環境所需的傳統 “getter 和 setter”。在這篇文章發表之後,我收到了 Bill Venners 發來的電子郵件,Bill Venners 是即將發表的正式的 Scala 參考資料使用 Scala 編程(請參閱 參考資料)的合著者之一。Bill 指出了實現上述操作的一個更簡單的方法,即使用 scala.reflect.BeanProperty 標注,如下所示:

清單 1. 修改後的 Person.scala

class Person(fn:String, ln:String, a:Int)
   {
   @scala.reflect.BeanProperty
   var firstName = fn

   @scala.reflect.BeanProperty
   var lastName = ln

   @scala.reflect.BeanProperty
   var age = a
   override def toString =
     "[Person firstName:" + firstName + " lastName:" + lastName +
     " age:" + age + " ]"
   }

清單 1 中的方法(上一篇文章 中的清單 13 的修訂版)為指定的 var 生成了 get/set 方法對。惟一的缺陷是這些方法並不實際存在於 Scala 代碼中,因此其他 Scala 代碼無法調用它們。這通常不是什麼大問題,因為 Scala 將對為自己生成的字段使用已生成的方法;如果事先不知道,那麼這些對您而言可能是一個驚喜。

在查看了清單 1 中的代碼之後,最讓我感到震動的是,Scala 並沒有只演示組合函數概念和對象概念的強大威力,它還演示了自 Java 首次發布之後的 30 年裡對象語言帶來的一些益處。

控制是一種幻想

您將看到的許多奇怪的、不可思議的東西都可以歸功於 Scala 的函數特性,因此,簡單介紹一下函數語言開發和演變的背景可能非常有用。

在函數語言中,將越來越高級的結構直接構建到語言中是不常見的。此外,語言是通過一組核心原語結構定義的。在與將函數作為對象傳遞的功能結合之後,可用來定義功能的高階函數 看起來 像是超出了核心語言的范圍,但實際上它只是一個庫。類似於任何庫,此功能可以替換、擴充或擴展。

根據一組核心原語構建語言的合成 特性由來已久,可以追溯到 20 世紀 60 年代和 70 年代使用 Smalltalk、Lisp 和 Scheme 的時候。諸如 Lisp 和 Scheme 之類的語言因為它們在更低級別的抽象上定義更高級別抽象的能力而受到人們的狂熱追捧。編程人員可以使用高級抽象,用它們構建更高級的抽象。如今聽到討論這個過程時,它通常是關於特定於域的語言(或 DSL)的(請參閱 參考資料)。實際上,它只是關於如何在抽象之上構建抽象的過程。

在 Java 語言中,惟一選擇就是利用 API 調用完成此操作;在 Scala 中,可以通過擴展語言本身實現它。試圖擴展 Java 語言會帶來創建極端場景(corner case)的風險,這些場景將威脅全局的穩定性。而試圖擴展 Scala 則只意味著創建一個新庫。

If 結構

我們將從傳統的 if 結構開始 —— 當然,此結構必須是最容易處理的結構之一,不是嗎?畢竟,從理論上說,if 只檢查一個條件。如果條件為真,則執行後面跟著的代碼。

但是,這種簡單性可能帶有欺騙性。傳統上,Java 語言對 if 的 else 子句的使用是隨意的,並且假定如果條件出錯,可以只跳過代碼塊。但在函數語句中,情況不是這樣。為了保持函數語句的算術特性,所有一切都必須以表達式計算的方式出現,包括 if 子句本身(對於 Java 開發人員,這正是三元操作符 —— ?: 表達式 —— 的工作方式)。

在 Scala 中,非真代碼塊(代碼塊的 else 部分)必須以與 if 代碼塊中值種類相同的形式呈現,並且必須產生同一種類的值。這意味著不論以何種方式執行代碼,總會產生一個值。例如,請參見以下 Java 代碼:

清單 2. 哪個配置文件?(Java 版)

// This is Java
String filename = "default.properties";
if (options.contains("configFile"))
  filename = (String)options.get("configFile");

因為 Scala 中的 if 結構自身就是一個表達式,所以重寫上述代碼會使它們成為清單 3 中所示的更正確的代碼片段:

清單 3. 哪個配置文件?(Scala 版)

// This is Scala
val filename =
  if (options.contains("configFile"))
   options.get("configFile")
  else
   "default.properties"

盡管真正的贏家是 Scala,但可以通過編寫代碼將結果分配給 val,而不是 var。在設置之後,就無法對 val 進行更改,這與 Java 語言中 final 變量的操作方式是相同的。不可變本地變量最顯著的副作用是很容易實現並發性。試圖用 Java 代碼實現同樣的操作時,會帶來許多不錯的、易讀的好代碼,如清單 4 中所示:

清單 4. 哪個配置文件?(Java 版,三元式)

//This is Java
final String filename =
  options.contains("configFile") ?
   options.get("configFile") : "default.properties";

用代碼評審解釋這一點可能需要點技巧。也許這樣做是正確的,但許多 Java 編程人員會不以為然並且詢問 “您做那個干什麼”?

已公開的 while 結構

接下來,讓我們來看一下 while 及其同胞 do-while。它們做的基本上是同一件事:測試一個條件,如果該條件為真,則繼續執行提供的代碼塊。

通常,函數語言會避開 while 循環,因為 while 實現的大多數操作都可以使用遞歸來完成。函數語言真地非常類似於 遞歸。例如,可以考慮一下 “Scala by Example”(請參閱 參考資料)中展示的 quicksort 實現,該實現可以與 Scala 實現一起使用:

清單 5. Quicksort(Java 版)

//This is Java
void sort(int[] xs) {
  sort(xs, 0, xs.length -1 );
}
void sort(int[] xs, int l, int r) {
  int pivot = xs[(l+r)/2];
  int a = l; int b = r;
  while (a <= b)
   while (xs[a] < pivot) { a = a + 1; }
   while (xs[b] > pivot) { b = b – 1; }
   if (a <= b) {
    swap(xs, a, b);
    a = a + 1;
    b = b – 1;
   }
  }
  if (l < b) sort(xs, l, b);
  if (b < r) sort(xs, a, r);
}
void swap(int[] arr, int i, int j) {
  int t = arr[i]; arr[i] = arr[j]; arr[j] = t;
}

不必深入太多的細節,就可以了解 while 循環的用法,它是通過數組中的各種元素進行迭代的,先找到一個支點,然後依次對每個子元素進行排序。毫不令人奇怪的是,while 循環也需要一組可變本地變量,在這裡,這些變量被命名為 a 和 b,其中存儲的是當前支點。注意,此版本甚至可以在循環自身中使用遞歸,兩次調用循環本身,一次用於對列表左手邊的內容進行排序,另一次對列表右手邊的內容進行排序。

這足以說明清單 5 中的 quicksort 真的不太容易讀取,更不用說理解它。現在來考慮一下 Scala 中的直接 等同物(這意味著該版本與上述版本盡量接近):

清單 6. Quicksort(Scala 版)

//This is Scala
def sort(xs: Array[Int]) {
  def swap(i: Int, j: Int) {
   val t = xs(i); xs(i) = xs(j); xs(j) = t
  }
  def sort1(l: Int, r: Int) {
   val pivot = xs((l + r) / 2)
   var i = l; var j = r
   while (i <= j) {
    while (xs(i) < pivot) i += 1
    while (xs(j) > pivot) j -= 1
    if (i <= j) {
   swap(i, j)
   i += 1
   j -= 1
    }
   }
   if (l < j) sort1(l, j)
   if (j < r) sort1(i, r)
  }
  sort1(0, xs.length 1)
}

清單 6 中的代碼看起來非常接近於 Java 版。也就是說,該代碼很長,很難看,並且難以理解(特別是並發性那一部分),明顯不具備 Java 版的一些優點。

So, I'll improve it ...

清單 7. Quicksort(更好的 Scala 版)

//This is Scala
def sort(xs: Array[Int]): Array[Int] =
  if (xs.length <= 1) xs
  else {
   val pivot = xs(xs.length / 2)
   Array.concat(
    sort(xs filter (pivot >)),
      xs filter (pivot ==),
    sort(xs filter (pivot <)))
  }

顯然,清單 7 中的 Scala 代碼更簡單一些。注意遞歸的使用,避免完全 while 循環。可以對 Array 類型使用 filter 函數,從而對其中的每個元素應用 “greater-than”、“equals” 和 “less-than” 函數。事實上,在引導裝入程序之後,因為 if 表達式是返回某個值的表達式,所以從 sort() 返回的是 sort() 的定義中的(單個)表達式。

簡言之,我已經將 while 循環的可變狀態完全再次分解為傳遞給各種 sort() 調用的參數 —— 許多 Scala 狂熱愛好者認為這是編寫 Scala 代碼的正確方式。

可能值得一提的是,Scala 本身並不介意您是否使用 while 代替迭代 —— 您會看到來自編譯器的 “您在干什麼,在做蠢事嗎?” 的警告。Scala 也不會阻止您在可變狀態下編寫代碼。但是,使用 while 或可變狀態意味著犧牲 Scala 語言的另一個關鍵方面,即鼓勵編寫具有良好並行性的代碼。只要有可能並且可行,“Scala 式作風” 會建議您優先在命令塊上執行遞歸。

編寫自己的語言結構

我想走捷徑來討論一下 Scala 的控制結構,做一些大多數 Java 開發人員根本無法相信的事 —— 創建自己的語言結構。

那些通過死讀書學習語言的書呆子會發現一件有趣的事:while 循環(Scala 中的一個原語結構)可能只是一個預定義函數。Scala 文檔以及假設的 “While” 定義中對此進行了解釋說明:

// This is Scala
def While (p: => Boolean) (s: => Unit) {
  if (p) { s ; While(p)(s) }
}

上述語句指定了一個表達式,該表達式產生了一個布爾值和一個不返回任何結果的代碼塊(Unit),這正是 while 所期望的。

擴展這些代碼行很容易,並且可以根據需要使用它們,只需導入正確的庫即可。正如前面提到的,這是構建語言的綜合方法。在下一節介紹 try 結構的時候,請將這一點牢記於心。

再三嘗試

try 結構允許編寫如下所示代碼:

清單 8. 如果最初沒有獲得成功……

// This is Scala
val url =
  try {
   new URL(possibleURL)
  }
  catch {
   case ex: MalformedURLException =>
    new URL("www.tedneward.com")
  }

清單 8 中的代碼與 清單 2 或 清單 3 中 if 示例中的代碼相差甚遠。實際上,它比使用傳統 Java 代碼編寫更具技巧,特別是在您想捕獲不可變位置上存儲的值的時候(正如我在 清單 4 中最後一個示例中所做的那樣)。這是 Scala 的函數特性的又一個優點!

清單 8 中所示的 case ex: 語法是另一個 Scala 結構(匹配表達式)的一部分,該表達式用於 Scala 中的模式匹配。我們將研究模式匹配,這是函數語言的一個常見特性,稍後將介紹它;現在,只把它看作一個將用於 switch/case 的概念,那麼哪種 C 風格的 struct 將用於類呢?

現在,再來考慮一下異常處理。眾所周知,Scala 支持異常處理是因為它是一個表達式,但開發人員想要的是處理異常的標准方法,並不僅僅是捕獲異常的能力。在 AspectJ 中,是通過創建方面(aspect)來實現這一點的,這些方面圍繞代碼部分進行聯系,它們是通過切入點定義的,如果想讓數據庫的不同部分針對不同種類異常采取不同行為,那麼必須小心編寫這些切入點 —— SQLExceptions 的處理應該不同於 IOExceptions 的處理,依此類推。

在 Scala 中,這只是微不足道的細節。請留神觀察!

清單 9. 一個自定義異常表達式

// This is Scala
object Application
{
  def generateException()
  {
   System.out.println("Generating exception...");
   throw new Exception("Generated exception");
  }
  def main(args : Array[String])
  {
   tryWithLogging // This is not part of the language
   {
    generateException
   }
   System.out.println("Exiting main()");
  }
  def tryWithLogging (s: => _) {
   try {
    s
   }
   catch {
    case ex: Exception =>
     // where would you like to log this?
   // I choose the console window, for now
   ex.printStackTrace()
   }
  }
}

與前面討論過的 While 結構類似,tryWithLogging 代碼只是來自某個庫的函數調用(在這裡,是來自同一個類)。可以在適當的地方使用不同的主題變量,不必編寫復雜的切入點代碼。

此方法的優點在於它利用了 Scala 的捕獲一級結構中橫切邏輯的功能 —— 以前只有面向方面的人才能對此進行聲明。清單 9 中的一級結構捕獲了一些異常(經過檢查的和未經檢查的都包括)並以特定方式進行處理。上述想法的副作用非常多,惟一的限制也許就是想象力了。您只需記得 Scala 像許多函數語言一樣允許使用代碼塊(aka 函數)作為參數並根據需要使用它們即可。

"for" 生成語言

所有這些都引導我們來到了 Scala 控制結構套件的實際動力源泉:for 結構。該結構看起來像是 Java 的增強 for 循環的簡單早期版,但它遠比一般的 Java 編程人員開始設想的更強大。

讓我們來看一下 Scala 如何處理集合上的簡單順序迭代,根據您的 Java 編程經驗,我想您應該非常清楚該怎麼做:

清單 10. 對一個對象使用 for 循環和對所有對象使用 for 循環

// This is Scala
object Application
{
  def main(args : Array[String])
  {
   for (i <- 1 to 10) // the left-arrow means "assignment" in Scala
    System.out.println("Counting " + i)
  }
}

此代碼所做的正如您期望的那樣,循環 10 次,並且每次都輸出一些值。需要小心的是:表達式 “1 to 10” 並不意味著 Scala 內置了整數感知(awareness of integer)以及從 1 到 10 的計數方式。從技術上說,這裡存在一些更微妙的地方:編譯器使用 Int 類型上定義的方法 to 生成一個 Range 對象(Scala 中的任何東西都是對象,還記得嗎?),該對象包含要迭代的元素。如果用 Scala 編譯器可以看見的方式重新編寫上述代碼,那麼該代碼看起來很可能如下所示:

清單 11. 編譯器看見的內容

// This is Scala
object Application
{
  def main(args : Array[String])
  {
   for (i <- 1.to(10)) // the left-arrow means "assignment" in Scala
    System.out.println("Counting " + i)
  }
}

實際上,Scala 的 for 並不了解那些成員,並且並不比其他任何對象類型做得更好。它所了解的是 scala.Iterable,scala.Iterable 定義了在集合上進行迭代的基本行為。提供 Iterable 功能(從技術上說,它是 Scala 中的一個特征,但現在將它視為一個接口)的任何東西都可以用作 for 表達式的核心。List、Array,甚至是您自己的自定義類型,都可以在 for 中使用。

特殊性

正如上面已經證明的那樣,for 循環可以做許多事情,並不只是遍歷可迭代的項列表。事實上,可以使用一個 for 循環在操作過程中過濾許多項,並在每個階段都產生一個新列表:

清單 12. 看一看還有哪些優點

// This is Scala
object Application
{
  def main(args : Array[String])
  {
   for (i <- 1 to 10; i % 2 == 0)
    System.out.println("Counting " + i)
  }
}

注意到清單 12 中 for 表達式的第二個子句了嗎?它是一個過濾器,實際上,只有那些傳遞給過濾器(即計算 true)的元素 “向前傳給” 了循環主體。在這裡,只輸出了 1 到 10 的偶數數字。

並不要求 for 表達式的各個階段都成為過濾器。您甚至可以將一些完全平淡無奇的東西(從循環本身的觀點來看)放入管道中。例如以下代碼顯示了在下一個階段進行計算之前的 i 的當前值:

清單 13. 讓我如何愛上您呢?別那麼冗長

// This is Scala
object App
{
  def log(item : _) : Boolean =
  {
   System.out.println("Evaluating " + item)
   true
  }
  def main(args : Array[String]) =
  {
   for (val i <- 1 to 10; log(i); (i % 2) == 0)
    System.out.println("Counting " + i)
  }
}

在運行的時候,范圍 1 到 10 中的每個項都將發送給 log,它將通過顯式計算每個項是否為 true 來 “批准” 每個項。然後,for 的第三個子句將對這些項進行篩選,過濾出那些滿足是偶數的條件的元素。因此,只將偶數傳遞給了循環主體本身。

簡單性

在 Scala 中,可以將 Java 代碼中復雜的一長串語句縮短為一個簡單的表達式。例如,以下是遍歷目錄查找所有 .scala 文件並顯示每個文件名稱的方法:

清單 14. Finding .scala

// This is Scala
object App
{
  def main(args : Array[String]) =
  {
   val filesHere = (new java.io.File(".")).listFiles
   for (
    file <- filesHere;
    if file.isFile;
    if file.getName.endsWith(".scala")
   ) System.out.println("Found " + file)
  }
}

這種 for 過濾很常見(並且在此上下文中,分號很讓人討厭),使用這種過濾是為了幫助您做出忽略分號的決定。此外,Scala 允許將上述示例中的圓括號之間的語句直接作為代碼塊對待:

清單 15. Finding .scala(版本 2)

// This is Scala
object App
{
  def main(args : Array[String]) =
  {
   val filesHere = (new java.io.File(".")).listFiles
   for {
    file <- filesHere
    if file.isFile
    if file.getName.endsWith(".scala")
   } System.out.println("Found " + file)
  }
}

作為 Java 開發人員,您可能發現最初的圓括號加分號的語法更直觀一些,沒有分號的曲線括號語法很難讀懂。幸運的是,這兩種句法產生的代碼是等效的。

一些有趣的事

在 for 表達式的子句中可以分配一個以上的項,如清單 16 中所示。

清單 16. 名稱中有什麼?

// This is Scala
object App
{
  def main(args : Array[String]) =
  {
   // Note the array-initialization syntax; the type (Array[String])
   // is inferred from the initialized elements
   val names = Array("Ted Neward", "Neal Ford", "Scott Davis",
    "Venkat Subramaniam", "David Geary")
   for {
    name <- names
    firstName = name.substring(0, name.indexOf(' '))
   } System.out.println("Found " + firstName)
  }
}

這被稱為 “中途賦值(midstream assignment)”,其工作原理如下:定義了一個新值 firstName,該值用於保存每次執行循環後的 substring 調用的值,以後可以在循環主體中使用此值。

這還引出了嵌套 迭代的概念,所有迭代都位於同一表達式中:

清單 17. Scala grep

// This is Scala
object App
{
  def grep(pattern : String, dir : java.io.File) =
  {
   val filesHere = dir.listFiles
   for (
    file <- filesHere;
    if (file.getName.endsWith(".scala") || file.getName.endsWith(".java"));
    line <- scala.io.Source.fromFile(file).getLines;
    if line.trim.matches(pattern)
   ) println(line)
  }
  def main(args : Array[String]) =
  {
   val pattern = ".*object.*"

   grep pattern new java.io.File(".")
  }
}

在此示例中,grep 內部的 for 使用了兩個嵌套迭代,一個在指定目錄(其中每個文件都與 file 連接在一起)中找到的所有文件上進行迭代,另一個迭代在目前正被迭代的文件(與 line 本地變量連接在一起)中發現的所有行上進行迭代。

使用 Scala 的 for 結構可以做更多的事,但目前為止提供的示例已足以表達我的觀點:Scala 的 for 實際上是一條管道,它在將元素傳遞給循環主體之前處理元素組成的集合,每次一個。此管道其中的一部分負責將更多的元素添加到管道中(生成器),一部分負責編輯管道中的元素(過濾器),還有一些負責處理中間的操作(比如記錄)。無論如何,Scala 會帶給您與 Java 5 中引入的 “增強的 for 循環” 不同的體驗。

匹配

今天要了解的最後一個 Scala 控制結構是 match,它提供了許多 Scala 模式匹配功能。幸運的是,模式匹配會聲明對某個值進行計算的代碼塊。首先,將執行代碼塊中最接近的匹配結果。因此,在 Scala 中可以包含以下代碼:

清單 18. 一個簡單的匹配

// This is Scala
object App
{
  def main(args : Array[String]) =
  {
   for (arg <- args)
    arg match {
   case "Java" => println("Java is nice...")
   case "Scala" => println("Scala is cool...")
   case "Ruby" => println("Ruby is for wimps...")
   case _ => println("What are you, a VB programmer?")
    }
  }
}

剛開始您可能將 Scala 模式匹配設想為支持 String 的 “開關’,帶有通常用作通配符的下劃線字符,而這正是典型開關中的默認情況。但是,這樣想會極大地低估該語言。模式匹配是許多(但不是大多數)函數語言中可以找到的另一個特性,它提供了一些有用的功能。

對於初學者(盡管這沒什麼好奇怪的),可能認為 match 表達式自身會產生一個值,該值可能出現在賦值語句的右邊,正如 if 和 try 語句所做的那樣。這一點本身也很有用,但匹配的真正威力體現在基於各種類型進行匹配時,而不是如上所述匹配單個類型的值,或者更多的時候,它是兩種匹配的組合。

因此,假設您有一個聲明返回 Object 的函數或方法 —— 在這裡,Java 的 java.lang.reflect.Method.invoke() 方法的結果可能是一個好例子。通常,在使用 Java 語言計算結果時,首先應該確定其類型;但在 Scala 中,可以使用模式匹配簡化該操作:

清單 19. 您是什麼?

//This is Scala
object App
{
  def main(args : Array[String]) =
  {
   // The Any type is exactly what it sounds like: a kind of wildcard that
   // accepts any type
   def describe(x: Any) = x match {
    case 5 => "five"
    case true => "truth"
    case "hello" => "hi!"
    case Nil => "the empty list"
    case _ => "something else"
   }

   println describe(5)
   println describe("hello")
  }
}

因為 match 的很容易簡單明了地描述如何針對各種值和類型進行匹配的能力,模式匹配常用於解析器和解釋器中,在那裡,解析流中的當前標記是與一系列可能的匹配子句匹配的。然後,將針對另一系列子句應用下一個標記,依此類推(注意,這也是使用函數語言編寫許多語言解析器、編譯器和其他與代碼有關的工具的部分原因,這些函數語言中包括 Haskell 或 ML)。

關於模式匹配,還有許多可說的東西,但這些會將我們直接引導至 Scala 的另一個特性 case 類,我想將它留到下次再介紹。

結束語

Scala 在許多方面看起來都非常類似於 Java,但實際上只有 for 結構存在一些相似性。核心語法元素的函數特性不僅提供了一些有用的特性(比如已經提到的賦值功能),還提供了使用新穎有趣的方式擴展語言的能力,不必修改核心 javac 編譯器本身。這使該語言更加符合 DSL 的定義(這些 DSL 是在現有語言的語法中定義的),並且更加符合編程人員根據一組核心原語(a la Lisp 或 Scheme)構建抽象的願望。

關於 Scala,有如此多的內容可以談論,但我們這個月的時間已經用完了。記得試用最新的 Scala bits(在撰寫本文時是 2.7.0-final)並嘗試提供的示例,感受一下該語言的操作(請參閱 參考資料)。請記住,到下一次的時候,Scala 會將一些有趣的(函數)特性放入編程中!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved