Var,Val 和 Def
由於 val
在語義上是靜態的,因此無論它們出現在程式碼中,它們都會就地初始化。當在抽象類和特徵中使用時,這會產生令人驚訝和不良行為。
例如,假設我們想製作一個名為 PlusOne
的特徵,它定義了一個包裝的 Int
的增量操作。由於 Int
s 是不可變的,所以在初始化時已知值加 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
。 (在其他情況下,它可能會填充 Nil
,null
或類似的預設值。)
因此,一般規則是避免在依賴於抽象欄位的任何值上使用 val
。相反,使用 lazy val
,它在需要時不進行評估,或使用 def
,每次呼叫時都會進行評估。但請注意,如果在初始化完成之前 lazy val
強制要求 lazy val
進行評估,則會發生相同的錯誤。