Я ищу некоторое представление о внутренних компонентах scala. Мы только что вышли на другую сторону болезненного сеанса отладки и выяснили, что наша проблема была вызвана неожиданным нулевым значением, которое, как мы думали, было бы предварительно инициализировано. Мы не можем понять, почему это так.Инициализированное значение класса оценивается как null при переопределенном методе
Ниже приведен пример примера, который иллюстрирует проблему (если он выглядит запутанным, потому что он намного сложнее в реальном коде, но я оставил основную структуру в том случае, если она значительна).
trait A {
println("in A")
def usefulMethod
def overrideThisMethod = {
//defaultImplementation
}
val stubbableFunction = {
//do some stuff
val stubbableMethod = overrideThisMethod
//do some other stuff with stubbableMethod
}
}
class B extends A {
println("in B")
def usefulMethod = {
//do something with stubbableFunction
}
}
class StubB extends B {
println("in StubB")
var usefulVar = "super useful" //<<---this is the val that ends up being null
override def overrideThisMethod {
println("usefulVar = " + usefulVar)
}
}
Если мы пнуть цепочки инициализации, это то, что выводится на консоль:
scala> val stub = new StubB
in A
usefulVar = null
in B
in StubB
Мои предположения
Я полагаю, что для того, чтобы создать экземпляр Стубб, первый мы создаем характеристику A, а затем B и, наконец, StubB: следовательно, порядок печати («в A», «в B», «в StubB»). Я предполагаю, что stubbableFunction
в признаке A оценивается при инициализации, потому что это значение val, то же самое для stubbableMethod
.
Отсюда я смущен.
Мой вопрос
Когда val overrideThisMethod
оценивается в признака А, я бы ожидать, что загрузчик классов следовать цепи вниз к Стубб (что он делает, вы можете сказать из-за печатанием «usefulVal = нуль»), но ... почему здесь значение null? Как можно оценить overrideThisMethod
в StubB без предварительной инициализации класса StubB и, следовательно, установить usefulVal
? Я не знал, что вы могли бы оценивать методы «осиротевших» таким образом - конечно, методы должны принадлежать классу, который должен быть инициализирован, прежде чем вы сможете вызвать метод?
Мы на самом деле решили проблему, изменив val stubbableFunction =
на def stubbableFunction =
в черте A, но нам все равно хотелось бы понять, что здесь происходит. Я с нетерпением жду возможности узнать что-то интересное о том, как Scala (или, может быть, Java) работает под капотом :)
Редактирование: Я изменил значение null на var и произошло то же самое - вопрос обновлен для ясности в ответ на Ответ mz
wow Ihor, мне нужно взять litle, чтобы переварить это. Просто комментарий, чтобы сказать, что useVal является val или var - я вижу такое же поведение. Я не знаю, повлияет ли это на любой из ваших ответов - я упоминаю об этом, потому что вы добавили этот ответ во время редактирования. Спасибо, я прочитаю ваш полный ответ, как только я получу чашку чая. – moncheery
'var' vs' val' ничего не меняет. Единственное, что меняется, это то, что для 'var x' генерируется сеттер с именем' x_ $ eq', но это не влияет ни на что в вашем случае. –
Awesome. Отличный ответ. Я знал, что это будет что-то интересное. Спасибо, Игорь – moncheery