Scala學(xué)習(xí)筆記 - A1篇

教材:快學(xué)Scala
markdown閱讀:https://www.zybuluo.com/mdeditor

chapter 1. 基礎(chǔ)

1.2 聲明值(val)和變量(var)

val xmax, ymax = 100 // 將xmax和ymax設(shè)為100
var greeting, message: string = null // greeting和message都是字符串郊尝,被初始化為null

1.3 常用類型

  • Byte Char Short Int Long Float Double Boolean 這些都是類
  • String: 使用底層的 java.lang.String 表示字符串揍诽,但通過 StringOps 類為字符串追加上百種操作
1.toString() // return "1"
1.to(10) // return Range(1,2,3,4,5,6,7,8,9,10)
"Hello".intersect("World") // 求交集 return "lo"

更多:

  • RichInt RichDouble RichChar 為Int Double Char 提供更多的方法(如前面的to())
  • BigInt BigDecimal 用于任意大小的數(shù)字

1.4 算術(shù)和操作符重載

  • 操作符+-*/都是方法 a+b 等價(jià)于 a.+(b)
  • a 方法 b 作為 a.方法(b) 的簡(jiǎn)寫 1.to(10) 可以寫成 1 to 10
  • 沒有 ++ --(自增/自減) 有 += -=

1.5 調(diào)用函數(shù)和方法

import scala.math._ // _是Scala的通配符台颠,類似Java的*
min(3, Pi)
pow(2, 4)
  • Scala沒有靜態(tài)方法瓷马,類似特性叫單例對(duì)象(singleton object),對(duì)應(yīng)有一個(gè)伴生對(duì)象(companion object)
  • e.g. BigInt類的BigInt伴生對(duì)象有一個(gè)生成指定位數(shù)的隨機(jī)素?cái)?shù)方法probablePrime
    BigInt.probablePrime(100, scala.util.Random)
  • 不帶參數(shù)且不改變當(dāng)前對(duì)象的Scala方法通常不使用圓括號(hào) e.g. "hello".distinct

1.6 apply方法

  • 如果s是一個(gè)字符串犬庇,s(i)代表s的第i個(gè)字符
    "HELLO"(4) // return 'O'
  • 原理:()操作符的重載形式呜袁,實(shí)現(xiàn)方式是一個(gè)名為apply的方法
  • "HELLO"(4) 等價(jià)于 "HELLO".apply(4)
  • BigInt的apply方法:BigInt(2).pow(1023)

chapter 2. 控制結(jié)構(gòu)和函數(shù)

  • C++/Java: 表達(dá)式(3+4) 和語句(if) 是兩種不同的東西敞临,表達(dá)式有值,語句執(zhí)行操作
  • Scala: 幾乎所有都是表達(dá)式(有值)

2.1 條件表達(dá)式(if)

val s = if (x > 0) 1 else -1 // preferred, val s, type: Int
// 等價(jià)于
if (x > 0) s = 1 else s = -1 // var s
  • if (x > 0) "positive" else -1 // type: Any 表達(dá)式類型為兩個(gè)分支的公共超類型:Any
  • if (x > 0) 1 等價(jià)于 if (x > 0) 1 else () ()看做是"無有用值"的占位符外盯,為Unit類摘盆,相當(dāng)于C++的void
  • Unitvoid的差異:void沒有值("錢包是空的") Unit有一個(gè)表示"無值"的值("錢包有一張寫著'沒錢(寫作())'的鈔票")

2.2 語句終止 每行末尾的分號(hào)不是必須的

2.3 塊表達(dá)式和賦值

  • { }塊表示一系列表達(dá)式,塊的值是最后一個(gè)表達(dá)式的值
  • 賦值語句的值是Unit類型

2.4 輸入和輸出

  • 輸出 print() println() printf()
  • 輸入 readLine() readInt() readDouble()

2.5 循環(huán)

  • while 語句:與C++一致
  • for 語句:for (1 <- 1 to n) 讓變量i遍歷<-右邊表達(dá)式的所有值饱苟,遍歷順序取決于表達(dá)式類型
  • 遍歷字符串或數(shù)組是孩擂,你需要0到n-1的區(qū)間,此時(shí)用until
val s = "Hello"
var sum = 0
for (i <- 0 until s.length) // i 最后一個(gè)取值是s.length - 1
     sum += s(i)
// 等價(jià)于
for (ch <- s) sum += ch
  • 沒有breakcontinue掷空,實(shí)現(xiàn)需要通過try/catch異常機(jī)制完成
    import scala.util.control.Breaks._

2.6 高級(jí)for循環(huán)/ for推導(dǎo)式

  • 多個(gè)生成器(<-)
for (i <- 1 to 3; j <- 1 to 3) print((10*i+j) + " ") // print 11 12 13 21 22 23 31 32 33
  • 每個(gè)生成器的守衛(wèi)(if)
for (i <- 1 to 3; j <- 1 to 3 if i != j) print((10*i+j) + " ") // print 12 13 21 23 31 32
  • 在循環(huán)中定義任意多的變量
for (i <- 1 to 3; from = 4 - i; j <- from to 3) print((10*i+j) + " ") // print 13 22 23 31 32 33
  • for推導(dǎo)式: 構(gòu)造出一個(gè)集合肋殴,每次迭代生成集合中的一個(gè)值
for (i <- 1 to 5) yield i % 3 // return Vector(1,2,0,1,2)
for (c <- "hello"; i <- 0 to 1) yield (c+i).toChar // ??? res40: String = hieflmlmop
for (i <- 0 to 1; c <- "hello") yield (c+i).toChar // ??? res41: scala.collection.immutable.IndexedSeq[Char] = Vector(h, e, l, l, o, i, f,  m, m, p)

2.7 函數(shù)

def fac(n: Int) = { // 非遞歸函數(shù)不需要指定返回類型
     var r = 1
     for (i <- 1 to n) r = r * i
     r // 不需要return
}

2.9 變長參數(shù)

def sum(args: Int*) = {
     var res = 0
     for (arg <- args) res += arg
     res
}
val s1 = sum(1, 4, 9, 16, 24) // 參數(shù)為Seq類型
val s2 = sum(1 to 5)       // wrong, 單個(gè)參數(shù)輸入時(shí),必須是Int類型
val s3 = sum(1 to 5: _*) // correct, 將Range當(dāng)做**參數(shù)序列**處理
def recursiveSum (args: Int*): Int = {
     if (args.length == 0) 0
     else args.head + recursiveSum(args.tail: _*) // head:第一個(gè)元素 tail: 其他元素
}

2.10 懶值 lazy val 推遲初始化

val words         = scala.io.Source.fromFile("/usr/share/dict/words").mkString // 在words被定義時(shí)即被取值
lazy val words = scala.io.Source.fromFile("/usr/share/dict/words").mkString // 在words首次使用時(shí)被取值
def words        = scala.io.Source.fromFile("/usr/share/dict/words").mkString // 在每一次words被使用時(shí)取值

chapter 3. 數(shù)組相關(guān)操作

3.1 定長數(shù)組 Array

val nums = new Array[Int](10)  // 長度為10坦弟,初始化為0
val a = new Array[String](10) // 長度為10护锤,初始化為null
val s = Array("hello", "world") // 長度為2,Array[String](類型推斷)已提供初始值不需要new
s(0) = "goobye" // Array("goobye", "world")

3.2 變長數(shù)組 ArrayBuffer

類似C++的vector酿傍,Java的ArrayList

import scala.collection.mutable.ArrayBuffer
val b = ArrayBuffer[Int]()
b += 1 // ArrayBuffer(1)
b += (1, 2, 3, 5) // ArrayBuffer(1, 1, 2, 3, 5)
b ++= Array(8, 13, 21) // ArrayBuffer(1, 1, 2, 3, 5, 8, 13, 21)
b ++= Seq(23, 25) // ArrayBuffer(1, 1, 2, 3, 5, 8, 13, 21, 23, 25)
b.trimEnd(7) // ArrayBuffer(1, 1, 2)
val bArr = b.toArray // Array(1, 1, 2) 轉(zhuǎn)換為Array
val bArrBuf = bArr.toBuffer // ArrayBuffer(1, 1, 2) 轉(zhuǎn)換為ArrayBuffer

3.3 遍歷

0 until 5 // Range(0, 1, 2, 3, 4)
0 until (5, 2) // Range(0, 2, 4)
(0 until 5).reverse // Range(4, 3, 2, 1, 0)

3.4 數(shù)組轉(zhuǎn)換

for推導(dǎo)式:for(...) yield 循環(huán)創(chuàng)建一個(gè)類型與原始集合相同的新集合

  • 原始集合:for語句括號(hào)內(nèi)的第一個(gè)生成器(<-)的表達(dá)式
for (i <- 1 to 5) yield i % 3 // return Vector(1,2,0,1,2)
for (c <- "hello"; i <- 0 to 1) yield (c+i).toChar // res40: String = hieflmlmop
for (i <- 0 to 1; c <- "hello") yield (c+i).toChar // res41: scala.collection.immutable.IndexedSeq[Char] = Vector(h, e, l, l, o, i, f,  m, m, p)
  • 原始集合 1 to 5 type:Range 新集合type:Vector
  • 原始集合 "hello" type:String 新集合type:String
  • 原始集合 0 to 1 type:Range 新集合type:Vector

3.5 常用算法

很大比例的業(yè)務(wù)運(yùn)算不過是在求和排序**
It is often said that a large percentage of business computations are nothing but computing sums and sorting.

Array(1, 7, 2, 9).sum // 19 ArrayBuffer同樣適用
Array("Mary", "had", "a", "little", "lamb").max // 'M' < 'a' < 'h' 'l'
val b = ArrayBuffer(1, 7, 2, 9)
val bSorted = b.sorted // ArrayBuffer(1, 2, 7, 9) 烙懦,b沒有被改變
val bDesc = b.sortWith(_ > _) // ArrayBuffer(9, 7, 2, 1)

可以直接對(duì)一個(gè) Array 排序,但不能對(duì) ArrayBuffer 排序

val a = b.toArray
scala.util.Sorting.quickSort(a) // Array(1, 2, 7, 9)

mkString 將數(shù)組轉(zhuǎn)換為字符串(類似js的join)
對(duì)于 min max quickSort 方法赤炒,元素類型必須支持比較操作氯析,即帶有 Ordered trait的類型

a.mkString(" and ") // "1 and 2 and 7 and 9"
a.mkString("<", ",", ">") // "<1,2,7,9>"

3.6 實(shí)用的Scaladoc解碼指環(huán) (page.36)

3.7 多維數(shù)組

  • 二維數(shù)組的類型:Array[Array[Int]] 構(gòu)造方法:ofDim
  • 支持創(chuàng)建每一行長度不一的數(shù)組
val matrix = Array.ofDim[Int](3, 4) // 3行 * 4列
matrix(row)(col) = 42 // 訪問元素

chapter 4. 映射和元組 Maps and Tuples

A classic programmer’s saying is, “If you can only have one data structure, make it a hash table.”

4.1 構(gòu)造Map

  • 映射(map)是對(duì)偶(pair)的集合亏较,對(duì)偶:n=2的元組(tuple)
  • -> 操作符創(chuàng)建pair。表達(dá)式 "alice" -> 10 的值為 ("alice",10)
    "alice" -> 10 // res11: (String, Int) = (alice,10)
  • 創(chuàng)建Map方法
    • 不可變Map
      val scores = Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8)
      或者
      val scores = Map(("Alice",10), ("Bob",3), ("Cindy",8))
    • 可變Map
      val scores = scala.collection.mutable.Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8)
    • 創(chuàng)建空的Map
      val scores = new scala.collection.mutable.Map[String, Int]

4.2 獲取Map中的值

val bobs = scores("Bob")
val bobs = scores.getOrElse("Bob", 0)
val bobs = scores.get("Bob") // res17: Option[Int] = Some(3)
val bobs = scores.get("Bobxxx") // res18: Option[Int] = None

4.3 更新Map中的值

  • 對(duì)于mutable.Map
scores("Bob") = 10 // update
scores("Fred") = 7 // add
// 等價(jià)于
scores += ("Bob" -> 10, "Fred" -> 7)
scores -= "Alice" // delete
  • 對(duì)于immutable.Map
val newScores = scores + ("Bob" -> 10, "Fred" -> 7)
val newScores = scores - "Alice"

4.4 迭代Map

for ((k, v) <- scores) print(k, v)
scores.keySet // res47: scala.collection.Set[String] = Set(Bob, Fred, Alice, Cindy)
scores.keys   // res49: Iterable[String] = Set(Bob, Fred, Alice, Cindy)
scores.values // res48: Iterable[Int] = HashMap(3, 10, 10, 8)
for (v <- scores.values) print(v + " ")
for ((k, v) <- scores) yield (v, k) // 交換key和value位置

4.5 Sorted Map

  • Map的實(shí)現(xiàn):哈希表 || 平衡樹掩缓,默認(rèn)是哈希表
  • 樹形映射只能創(chuàng)建為immutable
    val scores = scala.collection.immutalbe.SortedMap(...)
  • 按插入順序訪問所有key雪情,使用 LinkedHashMap
    val months = scala.collection.mutable.LinkedHashMap("Jan"->1, "Feb"->2, "Mar"->3, "Apr"->4)

4.7 tuple

  • 組元從1開始計(jì)數(shù),用方法_1你辣、_2巡通、_3訪問
val tp = (1, 3.14, "Fred") // tp: (Int, Double, String) = (1,3.14,Fred)
val second = tp._2 // second: Double = 3.14
val (first, second, third) = tp // first: Int = 1 second: Double = 3.14 third: String = Fred
val (first, second, _) = tp // 不關(guān)心的組元在其位置上使用_
  • StringOps的 partition 方法,返回一個(gè)pair舍哄,分別包含滿足和不滿足某個(gè)條件的字符
    "Hello World".partition(_.isUpper) // res53: (String, String) = (HW,ello orld)

4.8 zip操作

  • zip 將兩個(gè)大小相等的Array一一配對(duì)成pair組成新的Array
  • toMap 將pair的集合轉(zhuǎn)換成Map
val symbols = Array("<", "=", ">")
val counts = Array(2, 10, 2)
val pairs = symbols.zip(counts) // Array[(String, Int)] = Array((<,2), (=,10), (>,2))
for((s, n) <- pairs) print(s * n) // <<==========>>
val pairsMap = symbols.zip(counts).toMap // pairsMap: scala.collection.immutable.Map[String,Int] = Map(< -> 2, = -> 10, > -> 2)

chapter 5. 類 Class

5.1 簡(jiǎn)單類和無參方法

class Counter {
     private var value = 0 // **必須**初始化字段
     def inc() { value += 1 } // 無參方法宴凉,**默認(rèn)公有**
     def current() = value // 無參方法+1
}

val myCounter = new Counter // 構(gòu)造實(shí)例
myCounter.inc() // **改值器** 無參方法,后面加()
println( myCounter.current ) // **取值器** 無參方法表悬,不加()

5.2 帶getter和setter的屬性

  • 兩個(gè)概念:字段(fields):對(duì)應(yīng)C++的數(shù)據(jù)成員弥锄;屬性(properties):字段的getter/setter
  • 字段本身都是私有的。公有屬性(字段聲明不帶private)蟆沫,getter/setter是公有的籽暇;私有屬性(字段聲明帶private),getter/setter是私有的
  • 對(duì)于字段xxx饭庞,其getter名為 xxx 图仓,setter名為 xxx=
println(fred.age) // 調(diào)用方法fred.age
fred.age = 21 // 調(diào)用方法fred.age=(21)

5.3 只帶getter的屬性 — 只讀屬性的val字段

5.4 對(duì)象私有字段(Object-Private)

  • 與C++一樣,方法默認(rèn)可以訪問該類的所有對(duì)象的私有字段
    def isLess(other: Counter) = value < other.value // 可以訪問另一個(gè)對(duì)象other的私有字段value
  • 對(duì)象私有 private[this] :以上用法將被禁止但绕,方法只能訪問當(dāng)前對(duì)象(this)的私有字段value
  • 對(duì)象私有字段沒有g(shù)etter/setter
  • 更細(xì)粒度的訪問控制:private[類名] 類名:當(dāng)前定義的類/該類的外部類救崔,可指定這些類的方法訪問該字段

5.6 輔助構(gòu)造器

  • 主構(gòu)造器(primary constructor)有一個(gè),輔助構(gòu)造器(auxiliary constructor)有任意多個(gè)
  • 輔助構(gòu)造器的名稱是 this捏顺,每個(gè)輔助構(gòu)造器必須以已定義好的一個(gè)(主/輔助)構(gòu)造器的調(diào)用開始
  • 定義構(gòu)造器:def this(xxx) { } 注意沒有等號(hào)=六孵!
class Person {
    private var name = ""
    private var age = 0
    def this(name: String) { // 一個(gè)輔助構(gòu)造器
        this() //調(diào)用主構(gòu)造器
        this.name = name
    }
    def this(name: String, age: Int) { // 另一個(gè)輔助構(gòu)造器
        this(name) // 調(diào)用一個(gè)輔助構(gòu)造器
        this.age = age
    }
}
val p1 = Person()
val p2 = Person("Fred")
val p3 = Person("Petr", 20)

5.7 主構(gòu)造器

  • 主構(gòu)造器結(jié)合于類的定義中,沒有一個(gè)顯式的def
    • 主構(gòu)造器參數(shù)直接放置在類名之后
    • 主構(gòu)造器執(zhí)行在類定義中的所有語句
    • 易知幅骄,無論是使用何種構(gòu)造器劫窒,一定會(huì)執(zhí)行主構(gòu)造器的所有語句
class Person(val name: String, val age: Int) {
    println("Just constructed another person") // 每次有對(duì)象構(gòu)造出來都會(huì)執(zhí)行
    def this(name: String) {
        this(name, 0)
    }
    def this(bflag: Boolean) {
        this() // *出錯(cuò)* 定義帶參數(shù)主構(gòu)造器后,將沒有默認(rèn)的無參構(gòu)造器
    }
}
val p3 = new Person() // *出錯(cuò)* 沒有無參構(gòu)造器
val p1 = new Person("hello") // 輸出 "Just constructed another person"
val p2 = new Person("world", 10) // // 輸出 "Just constructed another person"
  • 類參數(shù)中有些是字段拆座,有些可能只是普通參數(shù)主巍,如何區(qū)分?
    • if(帶var/val的參數(shù)聲明) 私有字段挪凑,公有/私有的getter/setter
    • if(不帶var/val的參數(shù)聲明) { if(被至少一個(gè)方法使用) 對(duì)象私有字段 else 普通參數(shù) }

5.8 嵌套類

在類里面定義類孕索,默認(rèn)對(duì)象A的嵌套類和對(duì)象B的嵌套類互不相同

import scala.collection.mutable.ArrayBuffer
class Network {
    class Member(val name: String) {
        val contacts = new ArrayBuffer[Member]
    }
    private val members = new ArrayBuffer[Member]
    def join(name: String) = {
        val m = new Member(name)
        members += m
        m
    }
}
val chatter = new Network // 嵌套類 chatter.Member
val myFace = new Network // 嵌套類 myFace.Member
// chatter.Member 和 myFace.Member 是兩個(gè)不同的類
val fred = chatter.join("Fred")
val joe = chatter.join("Joe")
fred.contacts += joe // OK, fred 和 joe 都是chatter.Member類
val spinoza = myFace.join("Spinoza")
fred.contacts += spinoza // *出錯(cuò)* fred是chatter.Member類,spinoza是myFace.Member類
  • 讓chatter.Member和myFace.Member相同的方法
  • 1.將Member類定義移到Network的伴生對(duì)象
object Network {
    class Member(val name: String) {...}
}
class Network {
    private val members = new ArrayBuffer[Network.Member]
    ...
}
  • 2.使用類型投影(type projection) Network#Member 含義是"任何Network的Member"
class Network {
    class Member(val name: String) {
        val contacts = new ArrayBuffer[Network#Member]
    }
    ...
}
  • 在嵌套類使用外部類的字段:class Network { outer => outer變量指向 Network.this
class Network(val name: String) { outer =>
    class Member(val name: String) {
        def desc = name + " inside " + outer.name
    }
}

chapter 6. 對(duì)象 Object

object語法的用處
- 需要某個(gè)類的單個(gè)實(shí)例
- 為其他值或函數(shù)找一個(gè)可以掛靠的地方(when you want to find a home for miscellaneous values or functions)

6.1 單例對(duì)象

  • 對(duì)于C++中需要用到單例對(duì)象的地方躏碳,在Scala都可以通過定義一個(gè)object實(shí)現(xiàn)搞旭,作為單例模式或存放常量/方法的地方
  • 除了不能為構(gòu)造器提供參數(shù)以外,擁有與類一樣的所有特性,可以繼承類和trait
  • 對(duì)象構(gòu)造器在對(duì)象第一次使用時(shí)調(diào)用
object Accounts {
    private var lastNum = 0
    def newUniqueNum() = { lastNum += 1; lastNum }
}
Account.newUniqNum() // 當(dāng)需要一個(gè)新的唯一帳號(hào)時(shí)調(diào)用, 第一次調(diào)用前構(gòu)造器不會(huì)執(zhí)行

6.2 伴生對(duì)象 Companion Object

  • 實(shí)現(xiàn)類似C++的靜態(tài)方法
  • 同名的類和對(duì)象互為伴生肄渗,可以互相訪問私有方法/字段镇眷,必須存在于同一個(gè)源文件中
class Account {
    // 可以調(diào)用伴生對(duì)象的私有方法
    // 這里Account.是必須的,因?yàn)榘樯鷮?duì)象的方法不在此作用域中
    val id = Account.newUniqueNum()
    private var balance = 0.0
    def deposit(amount: Double) { balance += amount }
}
object Accounts {
    private var lastNum = 0
    private def newUniqueNum() = { lastNum += 1; lastNum }
}

6.3 繼承類/Trait的對(duì)象 Objects Extending a Class or Trait

  • 一個(gè)對(duì)象可以繼承(=1)個(gè)class和(>=0)個(gè)trait

6.4 apply方法

  • 當(dāng)遇到 Object(arg1, ..., argN) 時(shí)翎嫡,apply方法就會(huì)被調(diào)用
  • 構(gòu)造器和apply方法的區(qū)別:apply不用new欠动,Array(1, 2, 3)就是用了Array伴生對(duì)象的apply方法
  • Array(100)new Array(100)的區(qū)別
    • Array(100) 產(chǎn)生一個(gè)只有一個(gè)元素的數(shù)組Array[Int],元素值為100
    • new Array(100) 產(chǎn)生一個(gè)100個(gè)元素的數(shù)組Array[Nothing]惑申,值為null
abstract class UnitConversion(val fromUnitName: String, val toUnitName: String) {
    def convert(fromUnit: Double): Double // 包含定義不完整的方法翁垂,故為抽象類
    def apply(fromUnit: Double): Double = { // apply最終是在派生的object中使用
        println(fromUnit.toString + " " + fromUnitName + " = " + convert(fromUnit).toString + " " + toUnitName)
        convert(fromUnit)
    }
}
object InchesToCentimeters extends UnitConversion("inches", "centimeters") {
     // override def 重載父類方法
    override def convert(fromUnit: Double): Double = 2.54 * fromUnit
}
object GallonsToLiters extends UnitConversion("gallons", "liters") {
    override def convert(fromUnit: Double): Double = 3.785 * fromUnit
}
object FahrenheitToCelsius extends UnitConversion("fahrenheit", "celsius") {
    override def convert(fromUnit: Double): Double = (fromUnit - 32) / 1.8
}
InchesToCentimeters(1) // 調(diào)用 InchesToCentimeters.apply(1)
GallonsToLiters(2)
FahrenheitToCelsius(86)

6.5 應(yīng)用程序?qū)ο?Application Objects

Scala的Hello World程序,從一個(gè)對(duì)象的main開始硝桩,類型Array[String] => Unit

object Hello {
     def main(args: Array[String]) {
          println("Hello World")
     }
}

繼承App trait,可以不用定義main函數(shù)枚荣,直接把main代碼加到object構(gòu)造器方法體內(nèi)碗脊,即object的定義內(nèi)
原理:App extends DelayedInit(也是一個(gè)trait),所有帶有DelayedInit 這個(gè)trait的類橄妆,其初始化方法都會(huì)被挪到delayedInit方法中衙伶。App trait的main方法捕獲命令行參數(shù),調(diào)用delayedInit方法害碾。

object Hello extends App {
     println("Hello World")
}

6.6 枚舉

Scala沒有自帶枚舉類型矢劲,可通過Enumeration輔助生成枚舉類型

object PokerSuits extends Enumeration {
    type PokerSuits = Value // 聲明了該枚舉類型的名字為PokerSuits.PokerSuits,若不加這行則枚舉類型為PokerSuits.Value
    val Spade = Value("?") // 每個(gè)枚舉量的聲明慌随,參數(shù)為枚舉的名稱(可選)芬沉,ID(可選),默認(rèn)從0開始
    val Heart = Value("?") // id=1
    val Club = Value("?") // id=2
    val Dimond = Value("?") // id=3
}
import PokerSuits._  // 使用前要先import
def isRed(suite: PokerSuits.PokerSuits): Boolean = suite == Heart || suite == Dimond
isRed(Heart) // true
Spade.id // 1
Club.toString // ?

chapter 7. 包和引入 Packages and Imports

7.1 包 Packages

  • 作用與C++的namespace一樣:管理大型程序的名字阁猜。通過層層嵌套的package限定名稱丸逸。
  • 源文件的目錄和包之間沒有強(qiáng)制的關(guān)聯(lián)關(guān)系,一個(gè)源文件可以包含多個(gè)package的定義剃袍;一個(gè)package可以分散到多個(gè)源文件中定義黄刚。

7.2 作用域規(guī)則 Scopes

  • 與Java的包名總是絕對(duì)路徑不同,Scala支持相對(duì)路徑的包名(訪問上層作用域的名稱)民效。
package x {
    object obj_top {
        def fn = println("o1 func")
    }
    package y {
        package z {
            object obj {
                def fn = {
                    println("o2 func")
                    obj_top.fn
                }
            }
        }
    }
    package w {
        object obj {
            def fn = {
                println("o3 func")
                obj_top.fn
                y.z.obj.fn
            }
        }
    }
}
object Chap7Packages extends App {
    x.w.obj.fn
}

7.3 鏈?zhǔn)桨Z句

以串聯(lián)的形式聲明package憔维,被串聯(lián)在中間的package的成員將不能用相對(duì)路徑表示

package x.y {
    object obj {
        def fn = obj_top.fn // *錯(cuò)誤* 位于包x的obj_top不能直接可見
        def fn = x.obj_top.fn // ok
    }
}

7.4 文件頂部標(biāo)記法

適用于文件中所有的代碼屬于同一個(gè)包

package x.y
package z
object obj {}
// 以上代碼等價(jià)于
package x.y {
     package z {
          object obj {}
     }
}

7.5 包對(duì)象

  • 包可以包含class、objec畏邢、trait业扒,但不能包含函數(shù)和變量的定義
  • 如何把函數(shù)和變量定義在包里面?答案:包對(duì)象 package object
  • 每個(gè)包都可以有一個(gè)包對(duì)象舒萎,在父包中定義包對(duì)象凶赁,名字與對(duì)應(yīng)包一樣
  • 包對(duì)象和包處于同一個(gè)作用域,不需要加限定前綴
package object x { val defaultName = "xxx" }
package x {
     object obj_top {
          def fn = println( defaultName ) // 在包里面不需要加x.
     }
}
println( x.defaultName ) // 在包外面加上x. 就像是包里面直接定義的常量

7.6 包可見性

可以通過在public/protect/private加入限定包名稱,控制類成員在不同包層級(jí)的可見性

package y {
    package z {
        object obj {
            private[z] def fn = {} // 只在包z中可見
            private[y] def fn2 = {} // 可見度擴(kuò)展到y(tǒng)
        }
    }
}

7.7 引入 import

  • 目的:可以使用更短的名稱而不是長名稱
    import java.awt.Color._
  • import 可以在代碼的任意地方插入虱肄,控制import的作用域
  • 重命名引入方法(避免省略長名稱后可能引起的名稱混淆)
    import java.util.{HashMap => JavaHashMap}
  • 隱藏引入方法
    import java.util.{HashMap => _, _} // 隱藏HashMap致板,并且引入util的其他成員
  • 隱式引入,每個(gè)Scala程序都隱式import如下模塊:
import java.lang._
import scala._
import Predef._

chapter 8. 繼承 Inheritance

8.1 擴(kuò)展類 Extending a Class

class Employee extends Person {...}
可將類/方法/字段聲明為 final咏窿,確保其不能被派生/重寫

8.2 重寫方法 Overriding Methods

  • override 修飾符:用于重寫一個(gè)非抽象方法/字段
  • override必須保證方法名字和參數(shù)類型必須完全一致
  • override還可以在"易違約基類問題"(fragile base class problem)時(shí)給出錯(cuò)誤提示斟或,即在父類引入新方法時(shí),這個(gè)新方法與子類方法相抵觸(如父類新方法名字與子類已定義的方法重名)
  • super.父類方法 用于子類調(diào)用父類的方法

8.3 類型檢查和轉(zhuǎn)換

  • isInstanceOf 類型檢查:用于測(cè)試某個(gè)對(duì)象是否屬于某個(gè)給定的類
  • asInstanceOf 類型轉(zhuǎn)換:若測(cè)試成功集嵌,則用于將對(duì)象轉(zhuǎn)換為子類的對(duì)象
if (p.isInstanceOf[Employee]) { // true 當(dāng)p非null萝挤,且是Employee類或其子類的對(duì)象
     val s = p.asInstanceOf[Employee] // s類型為Employee,若p不是一個(gè)Employee則拋出異常
}
  • if (p.getClass == classOf[Employee]) 測(cè)試p是Employee對(duì)象而且不是其子類
  • a better alternative, 模式匹配:
p match {
     case s: Employee => ... // s 作為Employee處理
     case _ => ... // p 不是Employee
}

8.4 protected字段和方法

  • protected 成員可以被子類訪問根欧,但對(duì)于類所屬的包不可見——要用包修飾符才可見
  • protected[this] 類似 private[this]

8.5 父類構(gòu)造器

  • 在子類中怜珍,只有主構(gòu)造器才能調(diào)用父類構(gòu)造器,而且不能像Java super(params)一樣顯式調(diào)用父類構(gòu)造器
  • 父類構(gòu)造器的調(diào)用在子類主構(gòu)造器的定義中凤粗,可任意選擇父類構(gòu)造器
  • Scala類可以繼承Java類
// 1. 定義了子類Employee
// 2. 調(diào)用了父類構(gòu)造器Person(name, age)
`class Emploee(name: String, age: Int, val salavy: Double) extends Person(name, age) {...}

8.6 重寫字段

  • 可以在子類定義一個(gè)val重寫父類中同名的val
  • 重寫var:僅當(dāng)父類的var是抽象的才可以

8.7 匿名子類

val alien = new Person("Fred") {
     def greeting = "I am alien Fred."
}
def meet(p: Person{def greeting: String}) = println(p.name + "says: " + p.greeting)

匿名子類的類型為 Person{def greeting: String}

8.8 抽象類

  • 包含沒有完整定義的方法或字段(稱為抽象方法/抽象字段)酥泛,需要定義為抽象類 abstract class
  • 重寫抽象方法/字段時(shí)不需要加 override
  • 可以用匿名子類定制抽象字段
    val fred = new Person { val id = 1729; var name = "Fred" }

8.10 構(gòu)造順序和提前定義

  • 教訓(xùn):有可能會(huì)在子類重寫的val,不要在父類的構(gòu)造器中使用該val(如用該val定義一個(gè)Array的長度)
    • 涉及到Scala的子類/父類的成員構(gòu)造/初始化順序
    • 構(gòu)造順序的根本原因:Java允許在父類的構(gòu)造方法中調(diào)用子類的方法嫌拣,C++則不允許
  • "提前定義"語法 Early Definitions (略)
    • The syntax is so ugly that only a mother could love it.

8.11 Scala繼承層級(jí)

  • Any 整個(gè)繼承層級(jí)的根節(jié)點(diǎn)
  • AnyVal 所有值類型的一個(gè)marker
  • AnyRef 所有引用類型的父類
  • ScalaObject 所有Scala類都實(shí)現(xiàn)的marker接口
  • Unit C++的void由Unit表示柔袁,只有一個(gè)值(),AnyVal的子類异逐,編譯器允許任何值(Any 包括值和引用)被替換成()
def printUnit(x: Unit) = println(x)
printUnit("hello") // “hello"被替換成()捶索,打印出()
  • Null 所有引用類型(AnyRef)的子類型,唯一實(shí)例:值 null灰瞻,可以賦給任何引用類型但不能賦給值類型
  • Nothing 所有類型(Any)的子類型腥例,沒有實(shí)例。
    The Scala Inheritance Hierarchy.png

8.12 對(duì)象相等性

  • 重寫 equals 方法酝润,用來判斷兩個(gè)對(duì)象是否相等(不是相同)
  • 定義為 final院崇,為了父類對(duì)象(a)和子類對(duì)象(b) equals調(diào)用的對(duì)稱性,i.e. a.equals(b)和b.equals(a)結(jié)果相同
  • equals方法參數(shù)類型必須為 Any
final override def equals (other: Any) = {
     val that = other.asInstanceOf[Item]
     if (that == null) false
     else desc == that.desc && price == that.price
}

Chapter 9. 文件和正則表達(dá)式

9.1 讀取行/字符

import scala.io.Source
val source = Source.fromFile("myfile.txt", "UTF-8")
val source1 = Source.fromURL("http://horstmann.com", "UTF-8") // 從URL讀取
val source2 = Source.fromString("hello world") // 從String讀取袍祖,調(diào)試時(shí)很有用
val lines = source.getLines.toArray // 讀取行
for (c <- source) {...} // 處理字符
source.buffered // 查看某個(gè)字符但不處理
...
source.close() // 處理完畢要close

9.8 序列化 Serialization

  • 將對(duì)象序列化底瓣,用于傳輸?shù)狡渌麢C(jī)器或臨時(shí)存儲(chǔ)
  • Java通過 implements java.io.Serializable 聲明一個(gè)可被序列化的類
  • Scala: @SerialVersionUID(42L) class Person extends Serializable
  • Scala集合類都是可序列化的

9.9 執(zhí)行Shell命令

原理:sys.process 包含一個(gè)從String到 ProcessBuilder 對(duì)象的隱士轉(zhuǎn)換,! 操作符執(zhí)行的是隱式轉(zhuǎn)換后的 ProcessBuilder 對(duì)象

import sys.process._
"ls -al .." ! // 執(zhí)行結(jié)果打印到標(biāo)準(zhǔn)輸出蕉陋,返回結(jié)果:被執(zhí)行命令的返回值捐凭,成功0
val res = "ls -al .." !! // 執(zhí)行結(jié)果以字符串形式返回
"ls -al .." #| "grep sec" ! // 管道

還有重定向 #> #>> #<,以及 p #&& q(如果p成功則執(zhí)行q) p #|| q(如果p失敗則執(zhí)行q)

9.10 正則表達(dá)式

val numPattern = "[0-9]+".r
val wsnumwsPattern = """\s+[0-9]+\s+""".r // 用"""不需要轉(zhuǎn)義反斜杠
val matches = numPattern.findAllIn("99 bottles, 98 bottles").toArray // Array(99, 98)
val m1 = wsnumwsPattern.findFirstIn("99 bottles, 98 bottles").toArray // Some(" 98 ")

還有方法:findPrefixOf replaceFirstIn replaceAllIn (略)

9.11 正則表達(dá)式組

val numitemPattern = "([0-9]+) ([a-z]+)".r
val numitemPattern(num, item) = "99 bottles" // num: String = 99, item: String = bottles
for (numitemPattern(num, item) <- numitemPattern.findAllIn("99 bottles, 98 bottles")) println((num, item))
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末凳鬓,一起剝皮案震驚了整個(gè)濱河市茁肠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌缩举,老刑警劉巖垦梆,帶你破解...
    沈念sama閱讀 212,080評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件匹颤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡托猩,警方通過查閱死者的電腦和手機(jī)印蓖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來京腥,“玉大人赦肃,你說我怎么就攤上這事」耍” “怎么了他宛?”我有些...
    開封第一講書人閱讀 157,630評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長欠气。 經(jīng)常有香客問我厅各,道長,這世上最難降的妖魔是什么预柒? 我笑而不...
    開封第一講書人閱讀 56,554評(píng)論 1 284
  • 正文 為了忘掉前任队塘,我火速辦了婚禮,結(jié)果婚禮上卫旱,老公的妹妹穿的比我還像新娘。我一直安慰自己围段,他們只是感情好顾翼,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,662評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著奈泪,像睡著了一般适贸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涝桅,一...
    開封第一講書人閱讀 49,856評(píng)論 1 290
  • 那天拜姿,我揣著相機(jī)與錄音,去河邊找鬼冯遂。 笑死蕊肥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蛤肌。 我是一名探鬼主播壁却,決...
    沈念sama閱讀 39,014評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼裸准!你這毒婦竟也來了展东?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,752評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤炒俱,失蹤者是張志新(化名)和其女友劉穎盐肃,沒想到半個(gè)月后爪膊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,212評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡砸王,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,541評(píng)論 2 327
  • 正文 我和宋清朗相戀三年拍谐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坛猪。...
    茶點(diǎn)故事閱讀 38,687評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蛙紫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出荷辕,到底是詐尸還是另有隱情凿跳,我是刑警寧澤,帶...
    沈念sama閱讀 34,347評(píng)論 4 331
  • 正文 年R本政府宣布疮方,位于F島的核電站控嗜,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏骡显。R本人自食惡果不足惜疆栏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,973評(píng)論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望惫谤。 院中可真熱鬧壁顶,春花似錦、人聲如沸溜歪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蝴猪。三九已至调衰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間自阱,已是汗流浹背嚎莉。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沛豌,地道東北人趋箩。 一個(gè)月前我還...
    沈念sama閱讀 46,406評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像加派,于是被迫代替她去往敵國和親阁簸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,576評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • Scala與Java的關(guān)系 Scala與Java的關(guān)系是非常緊密的:哒伞启妹! 因?yàn)镾cala是基于Java虛擬機(jī),也就是...
    燈火gg閱讀 3,427評(píng)論 1 24
  • Spark SQL, DataFrames and Datasets Guide Overview SQL Dat...
    草里有只羊閱讀 18,305評(píng)論 0 85
  • 1.1. 什么是Scala Scala是一種多范式的編程語言醉旦,其設(shè)計(jì)的初衷是要集成面向?qū)ο缶幊毯秃瘮?shù)式編程的各種特...
    文子軒閱讀 1,527評(píng)論 1 3
  • 藝術(shù)作品是獨(dú)特脾性的獨(dú)特結(jié)果饶米。 所有的藝術(shù)都是沒什么用的桨啃。 我只想指出這么一個(gè)一般性的原則,就是生活模仿藝術(shù)的程度...
    路邊詩社閱讀 577評(píng)論 1 5
  • 今天陪琛寶看了三本可可獅的配套繪本檬输,還有一本猜猜我是誰是琛寶自己親自翻出來要看的照瘾。
    羅mi閱讀 159評(píng)論 0 0