2017-02-18 2 views
1

Я читаю Программирование в Scala, Third Edition, от Lex Spoon; Билл Веннерс; Мартин Одерски, и пример из примера.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. Вы можете увидеть это, посмотрев на stacktrace и отметив, что NPE выбрано в определении width в абстрактном классе.

Когда line определен как метод, он не выполняется до тех пор, пока вы его не назовете, и к тому времени содержимое будет полностью определено, поскольку оно может вызвать line (другой метод), который будет полностью определен.

Итог: у вас есть своего рода «круговой зависимости» между line и contents.

+0

вид имеет смысл. Я удалил переопределения из параметров конструктора и добавил их в тело класса. И это работает без ошибок. (добавленный вопрос с обновленным определением класса). Я думаю, это правильный путь? Спасибо большое! –

+0

да, это, кажется, правильный путь! – acidghost

Смежные вопросы