2017-02-18 77 views
1

我在閱讀編程斯卡拉,第三版,由Lex勺子;比爾威納斯;馬丁Odersky,並嘗試沿途的例子。NPE中使用scala Array.fill

繼例如形成書

abstract class Element { 
    def contents: Array[String] 
    val height = contents.length 
    val width = if (height == 0) 0 else contents(0).length 
} 

class UniformElement(
    ch: Char, 
    override val width: Int, 
    override val height: Int 
) extends Element { 
    private val line = ch.toString * width 
    def contents = Array.fill(height)(line) 
} 

val e: Element = new UniformElement('x', 2, 3) 

顯示java.lang.NullPointerException,在REPL嘗試時,或在Eclipse中的工作表。

但是,如果我改變

private val line = ch.toString * width 

private def line = ch.toString * width 

沒有錯誤observerd。

我無法理解爲什麼? 有人可以解釋嗎?

我使用Scala的2.11.8


編輯

答案後從@acidghost,我更改如下類UniformElement,並沒有得到NPE。 :)

class UniformElement(
    ch: Char, 
    val w: Int, 
    val h: Int 
) extends Element { 
    override val width: Int = w 
    override val height: Int = h 
    private val line = ch.toString * width 
    def contents = Array.fill(height)(line) 
} 

回答

4

的這裏的問題是,contents仍然沒有在構造函數中定義的,當你定義line。如果行是val,它不會選擇重寫的width,而是使用抽象的那個,然後使用contents,該抽象的仍然未定義,並且您獲得NPE。你可以通過查看棧跟蹤並注意到抽象類中的width的定義拋出NPE。

line被定義爲一種方法,它不執行,直到你把它和那個時候的內容將被完全定義,因爲它可以調用line(另一種方法),這將完全定義。底線:您在linecontents之間有一種「循環依賴關係」。

+0

有道理。 我從構造函數參數中刪除了重寫,並將其添加到類體中。那沒有錯誤。 (帶更新的類定義的附加問題)。 我想,這是正確的方法? 非常感謝! –

+0

是的,這似乎是正確的方式! – acidghost