學習Scala第一篇-從hello World開始

黃博文發表於2015-11-15

最近開始系統性的學習scala。其實之前使用過scala的,比如我在用Gatling這款效能測試工具的時候就接觸到了scala了。Gatling本身就是用Scala寫的,而且Gatling的效能測試配置檔案本身就是一個scala類,可以隨意使用scala甚至是Java提供的各種類庫。當時覺得用Gatling特別舒服的原因就在於配置檔案強大的表現力。而這種表現力就是由Scala語言提供的。

言歸正傳,學習Scala還是從最簡單的Hello world開始。在Scala官網中顯著的標題就是:

Object-Oriented Meets Functional

Have the best of both worlds. Construct elegant class hierarchies for maximum code reuse and extensibility, implement their behavior using higher-order functions. Or anything in-between.

從中可以看出Scala結合了物件導向及函數語言程式設計這兩種程式設計正規化。在本文中我將會拿Java語言和其比較,看看到底Scala強在那裡。

使用Java語言實現一個Hello World的程式碼如下:

1
2
3
4
5
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

以下是Scala的實現:

1
2
3
4
5
object HelloWorld  {
  def main(args: Array[String]): Unit = {
    println("Hello, World!")
  }
}

好吧,如果標點符號不算程式碼行,Java版本和Scala版本的實現的程式碼行數是一致的。貌似Scala並沒有減少什麼。唯一減少的就在於println不用指定類名,以及對main方法不用宣告為static。

在這裡對Scala實現與Java實現的幾個不同之處做個介紹。

  • 第一是Scala對HelloWorld的修飾符使用的是object。其實Scala中也有class關鍵字,那麼object關鍵字和class關鍵字有什麼區別那?簡單來說object關鍵字定義了一個匿名類,並且建立了該匿名類的單個例項(採用單例模式),該例項名為HelloWorld。所以object中定義的方法自動都是static的。我覺得object關鍵字存在的價值之一就是建立起了物件導向和函式式的橋樑。因為在物件導向的系統中,所有方法都必須存在於類中,而函數語言程式設計中沒有類的概念,使用函式無需new類的例項,所以object中的方法都是靜態方法,可以直接被呼叫。進一步解讀請到這裡

  • 第二是Scala中對變數的型別的定義方式是變數名在前,型別在後,中間用冒號相隔。原因之一是程式碼更可讀。因為我們更關心變數名,而型別其次,尤其是你擁有一個超級長的型別的情況下(比如 HashMap<Shape, Pair<String, String>>);原因之二據說是這樣的方式在實現Scala型別時技術上要簡單些。進一步解讀請到這裡

  • 第三是main函式的返回值是Unit,而不是Java中的Void。為什麼是這樣那?我想是因為Scala為了實現自己的型別系統,對於無顯式返回值的函式直接使用Void是不合適的。在Scala.Unit文件中是這樣定義Unit的:

Unit is a subtype of scala.AnyVal. There is only one value of type Unit, (), and it is not represented by any object in the underlying runtime system. A method with return type Unit is analogous to a Java method which is declared void.

  • 第四是Scala中表示式最後的分號是可選的。原因就是為了契合函數語言程式設計的哲學,即一切儘可能的簡單。不寫分號程式設計師一天可以多敲一些程式碼出來。

好吧,其實在Scala中Hello World還有一種寫法。

1
2
3
object HelloWordV2 extends App {
  println("Hello, World!")
}

程式碼從三行減少到了兩行,Scala終於勝出了。那麼App是個什麼鬼?App是一個trait。trait又是什麼鬼?trait是Scala中的一個特殊型別,它與Java中的interface很相似,但比interface強大。HelloWordV2新增了對App的擴充套件後,就自動成為了一個可以執行的程式,由於App中定義了main方法,所以HelloWorldV2中就無需再定義了,牛逼的地方在於HelloWorldV2的body中的程式碼都會作為main方法中的程式碼被執行。以下是App原始碼中對main方法的定義:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@deprecatedOverriding("main should not be overridden", "2.11.0")
def main(args: Array[String]) = {

this._args = args

for (proc <- initCode) proc()

if (util.Properties.propIsSet("scala.time")) {

val total = currentTime - executionStart

Console.println("[total " + total + "ms]")

}

看來App在實現main方法時還設定了一個計時器,通過scala.time這個屬性來開關。所以沒事翻翻原始碼還是挺好玩的。

不知有沒注意到其實main方法是有個叫args的引數的,那麼新版HelloWorld中如何使用該引數那?直接使用它就行。

1
2
3
object HelloWordV2 extends App {
  println("Hello, World!" + args(0))
}

ok,Scala版的Hello World就到這裡吧。

相關文章