上一篇
目錄
26. 聯合型別
❌ 本部分尚未完成,但你可以通過 Miles 的部落格(下方有連結)獲得更多瞭解 :-)
讓我們開始來討論這個型別,這次要藉助下「集合論」,然後把我們已經學過的 A with B
當做一個「交集型別」。
為什麼呢?因為只有同時帶有型別 A
和型別 B
的物件才能符合這個型別,因此在集合論中,它將是一個交集。此外,我們思考下什麼是「聯合型別」。
這是兩個集合的聯合,逐集看每個元素的型別要麼是 A
或者 B
。我們的任務是使用 Scala 型別系統來引入這樣的型別。雖然它們不是 Scala 中的第一等構造(不是內建的),我們也可以很容易地實現和使用它們。Miles Sabin 在部落格中通過「 Curry-Howard 同構」深入地解釋了這一技術,如果你比較好奇,可以去看看。
type |∨|[T, U] = { type λ[X] = ¬¬[X] <:< (T ∨ U) }
def size[T : (Int |∨| String)#λ](t : T) = t match {
case i : Int => i
case s : String => s.length
}複製程式碼
27. 延遲初始化
自從我們開始討論 Scala 中一些不常見的型別,我們就會安排專門的章節來介紹每一個型別。延遲初始化(Delayed Init)實際上只是一種編譯器的技巧而已,它對型別系統而言並不是非常重要。但是一旦你理解了它,就會明白 scala.App
是如何運作的,所以看看下面的例子吧:
object Main extends App {
println("Hello world!")
}複製程式碼
檢視這段程式碼,根據我們已知的 Scala 基礎知識,會下這樣結論:「那麼,println
是在 Main
類的建構函式中!」。這通常是對的,但在這裡卻並不是這樣的,因為 App
繼承了 DelayedInit
特質:
trait App extends DelayedInit {
// code here ...
}複製程式碼
讓我們來看看延遲初始化的特質的完整原始碼:
trait DelayedInit {
def delayedInit(x: => Unit): Unit
}複製程式碼
正如你所見,它並沒有包含任何的實現 — 所有圍繞它的工作實際上都是編譯器執行的,它將以一種特殊的方式來對待「繼承了 DelayedInit
」的類和單例(注:特質不會像這樣一樣重寫)。
特殊待遇是這樣子的:
- 假設你的類/單例主體是一個函式,處理主體中要做的所有事情
- 編譯器為你建立了這個函式,並將它傳遞給了延遲初始化方法(x: => Unit),
我們馬上來舉個例子,手動來重新實現一遍 App
為我們自動做的事情(在 delayedInit
的幫助下):
// we write:
object Main extends DelayedInit {
println("hello!")
}
// the compiler emits:
object Main extends DelayedInit {
def delayedInit(x: => Unit = { println("Hello!") }) = // impl is left for us to fill in
}複製程式碼
使用這種機制,你可以隨時執行你的類的主體。我們已經瞭解了延遲初始化如何工作,接下來再來實現下我們自己版本的 scala.App
吧(這實際上也是以相同方式實現的)。
override def delayedInit(body: => Unit) {
initCode += (() => body)
}
def main(args: Array[String]) = {
println("Whoa, I'm a SimpleApp!")
for (proc <- initCode) proc()
println("So long and thanks for all the fish!")
}
}
// Running the bellow class would print print:
object Test extends SimpleApp { //
// Whoa, I'm a SimpleApp!
println(" Hello World!") // Hello World!
// So long and thanks for all the fish!
}複製程式碼