2016-03-02 124 views
4

一个奇怪的NullPointerException异常的代码放在这里:在斯卡拉

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

    override def toString = contents(0) 
} 
class ArrayElement(override val contents: Array[String]) extends Element 

class LineElementT(s: String) extends Element { 
    override def contents = Array(s) 
} 

class LineElementF(s: String) extends Element { 
    override val contents = Array(s) 
} 

这三个子类都OK,除了LineElementF,与val lef = new LineElementF("Wrong")

回答

4

这NPE创建实例时抛出NullPointerException发生在高度初始化。在高度初始化时,LineElementF的val内容未被初始化。

可以通过声明都高度避免这种NPE和宽度懒:

lazy val height = contents.length 
lazy val width = if(height ==0) 0 else contents(0).length 
+0

谢谢,但你能告诉我为什么其他两个子类运作良好吗? – monk

+0

区别在于,你将'contents'一次定义为'val',一次定义为'def。 'val'的右边部分在施工时进行评估,这是'def'at通话时间之一。您可以将内容声明为“lazy val contents”,以便在第一次调用它时仅对它进行一次评估。 –

+0

仍然困惑,但非常感谢 – monk

1

Scala语言也有一个特殊的语法(有时称为early definitions or early initilizer),它允许前执行子类的构造函数其超类的构造函数:

class LineElementEI(s: String) extends { 
    override val contents = Array(s) 
} with Element 

澄清为何经常“迟到初始化”不与你的工作LineElementF:在超的身体,你正试图评估contents.length,但子类的正文仅在超类后评估,所以contents仍然是null。您可以解决这个问题,可以在依赖于子类初始化的所有字段上使用惰性评估,或者如上所示更改初始化顺序。