Var,Val 和 Def

由於 val 在語義上是靜態的,因此無論它們出現在程式碼中,它們都會就地初始化。當在抽象類和特徵中使用時,這會產生令人驚訝和不良行為。

例如,假設我們想製作一個名為 PlusOne 的特徵,它定義了一個包裝的 Int 的增量操作。由於 Ints 是不可變的,所以在初始化時已知值加 1,之後永遠不會改變,因此在語義上它是一個 val。但是,以這種方式定義它將產生意想不到的結果。

trait PlusOne {
    val i:Int

    val incr = i + 1
}

class IntWrapper(val i: Int) extends PlusOne

無論你使用什麼價值 i,在返回的物件上呼叫 .incr 將始終返回 1.這是因為 val incr 在特徵中初始化,在擴充套件類之前,並且此時 i 僅具有預設值 0 。 (在其他情況下,它可能會填充 Nilnull 或類似的預設值。)

因此,一般規則是避免在依賴於抽象欄位的任何值上使用 val。相反,使用 lazy val,它在需要時不進行評估,或使用 def,每次呼叫時都會進行評估。但請注意,如果在初始化完成之前 lazy val 強制要求 lazy val 進行評估,則會發生相同的錯誤。

可以在這裡找到一個小提琴(用 Scala-Js 編寫,但同樣的行為適用)