1. 程式人生 > >Scala——基礎篇

Scala——基礎篇

表示式和值

在Scala中,幾乎所有的語言元素都是表示式。

println("hello wolrd")

是一個表示式,

"hello"+" world"

也是一個表示式。

可以通過val定義一個常量,亦可以通過var定義一個變數,推薦多使用常量。

var helloWorld = "hello" + " world" 
println(helloWorld)

val again = " again" 
helloWorld = helloWorld + again
println(helloWorld)



函式是一等公民

可以使用def來定義一個函式。函式體是一個表示式。

使用Block表示式的時候,預設最後一行的返回是返回值,無需顯式指定。

函式還可以像值一樣,賦值給var或val。因此函式也可以作為引數傳給另一個函式。

def square(a: Int) = a * a

def squareWithBlock(a: Int) = {
    a * a
}

val squareVal = (a: Int) => a * a

def addOne(f: Int => Int, arg: Int) = f(arg) + 1

println("square(2):" + square(2))
println("squareWithBlock(2):" + squareWithBlock(2))
println("squareVal(2):" + squareVal(2))
println("addOne(squareVal,2):" + addOne(squareVal, 2))

借貸模式

由於函式可以像值一樣作為引數傳遞,所以可以方便的實現借貸模式。

這個例子是從/proc/self/stat檔案中讀取當前程序的pid。

withScanner封裝了try-finally塊,所以呼叫者不用再close

注:當表示式沒有返回值時,預設返回Unit。

import scala.reflect.io.File
import java.util.Scanner

def withScanner(f: File, op: Scanner => Unit) = {
    val scanner = new Scanner(f.bufferedReader)
    try {
        op(scanner)
    } finally {
        scanner.close()
    }
}

withScanner(File("/proc/self/stat"),
    scanner => println("pid is " + scanner.next()))

按名稱傳遞引數

這個例子演示了按名稱傳遞引數,由於有除以0,所以執行該程式會產生異常。

試著將

def log(msg: String)

修改為

def log(msg: => String)

由按值傳遞修改為按名稱傳遞後將不會產生異常。

因為log函式的引數是按名稱傳遞,引數會等到實際使用的時候才會計算,所以被跳過。

按名稱傳遞引數可以減少不必要的計算和異常。

val logEnable = false

def log(msg: String) =
    if (logEnable) println(msg)

val MSG = "programing is running"

log(MSG + 1 / 0)

定義類

可以用class關鍵字來定義類。並通過new來建立類。

在定義類時可以定義欄位,如firstName,lastName。這樣做還可以自動生成建構函式。

可以在類中通過def定義函式var和val定義欄位

函式名是任何字元如+,-,*,/。

試著將

obama.age_=(51)

簡化為

obama.age = 51

這樣的簡化更像呼叫一個變數。

class Persion(val firstName: String, val lastName: String) {

    private var _age = 0
    def age = _age
    def age_=(newAge: Int) = _age = newAge

    def fullName() = firstName + " " + lastName

    override def toString() = fullName()
}

val obama: Persion = new Persion("Barack", "Obama")

println("Persion: " + obama)
println("firstName: " + obama.firstName)
println("lastName: " + obama.lastName)
obama.age_=(51)
println("age: " + obama.age)

鴨子型別

走起來像鴨子,叫起來像鴨子,就是鴨子。

這個例子中使用

{ def close(): Unit }

作為引數型別。因此任何含有close()的函式的類都可以作為引數。

不必使用繼承這種不夠靈活的特性。


def withClose(closeAble: { def close(): Unit }, 
    op: { def close(): Unit } => Unit) {
    try {
        op(closeAble)
    } finally {
        closeAble.close()
    }
}

class Connection {
    def close() = println("close Connection")
}

val conn: Connection = new Connection()
withClose(conn, conn =>
    println("do something with Connection"))

柯里化

這個例子和上面的功能相同。不同的是使用了柯里化(Currying)技術。

def add(x:Int, y:Int) = x + y

是普通的函式

def add(x:Int) = (y:Int) => x + y

是柯里化後的函式,相當於返回一個匿名函式表示式。

 def add(x:Int)(y:Int) = x + y

是簡化寫法

柯里化可以讓我們構造出更像原生語言提供的功能的程式碼

試著將例子中的withclose(...)(...)換成withclose(...){...}


def withClose(closeAble: { def close(): Unit })
    (op: { def close(): Unit } => Unit) {
    try {
        op(closeAble)
    } finally {
        closeAble.close()
    }
}

class Connection {
    def close() = println("close Connection")
}

val conn: Connection = new Connection()
withClose(conn)(conn =>
    println("do something with Connection"))

範型

之前的例子可以使用泛型變得更簡潔更靈活。

試著將

"123456"

修改為

123456

雖然msg由String型別變為Int型別,但是由於使用了泛型,程式碼依舊可以正常執行。

def withClose[A <: { def close(): Unit }, B](closeAble: A)
  (f: A => B): B =
  try {
    f(closeAble)
  } finally {
    closeAble.close()
  }
class Connection {
  def close() = println("close Connection")
}
val conn: Connection = new Connection()
val msg = withClose(conn) { conn =>
  {
    println("do something with Connection")
    "123456"
  }
}

println(msg)


Traits

Traits就像是有函式體的Interface。使用with關鍵字來混入。

這個例子是給java.util.ArrayList添加了foreach的功能。

試著再在with ForEachAble[Int]後面加上

with JsonAble

給list新增toJson的能力

trait ForEachAble[A] {
  def iterator: java.util.Iterator[A]
  def foreach(f: A => Unit) = {
    val iter = iterator
    while (iter.hasNext)
      f(iter.next)
  }
}

trait JsonAble {
  def toJson() =
    scala.util.parsing.json.JSONFormat.defaultFormatter(this)
}

val list = new java.util.ArrayList[Int]() with ForEachAble[Int] 
list.add(1); list.add(2)

println("For each: "); list.foreach(x => println(x))
//println("Json: " + list.toJson())