2017-02-18 69 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