Практически не существует идеальной практики в Скале, чтобы оставить частично построенные объекты, лежащие вокруг. Обычно вы переосмысливаете, как ваши объекты создавались, чтобы увидеть, не можете ли вы использовать другой шаблон, который менее хрупкий. Например, вместо установки неинициализированных переменных в методах:
class Foo { var a: String = null; var b: String = null }
def initFooA(s: String, f: Foo) { if (f.a == null) f.a = s }
def initFooB(s: String, f: Foo) { if (f.b == null) f.b = s }
f
initFooA("salmon", f)
// Do stuff
initFooB("herring", f)
вы пытаетесь перестроить свой код, чтобы сгенерировать значения, нужно по требованию, а не задерживать экземпляра обув до тех пор:
case class Bar(a: String, b: String) {}
def initBarA(s: String) = s
def initBarB(s: String) = s
val iba = initBarA("halibut")
// Do stuff
val ibb = initBarB("cod")
Bar(iba, ibb)
Поскольку Scala имеет легкий доступ к кортежам (и выводит тип), это может быть намного менее болезненным, чем в Java.
Другая вещь, которую вы можете сделать, - отложить позднюю инициализацию кому-то еще.
case class Baz(a: String)(bMaker: => String) {
lazy val b = bMaker
}
Теперь вы передаете то, что сделает параметр Ь, и организовать для того, чтобы справиться с любым поздним материалом инициализации, который должен быть обработан. Это не всегда не нужно устанавливать vars, но это может помочь вытолкнуть его из вашего кода класса в вашу логику инициализации (что обычно лучше для него).
Выполнение этого с помощью vars немного менее прямолинейно. Реально, вам, вероятно, лучше всего посвятить класс этому, например. по:
class LazyVar[A](initial: => A) {
private[this] var loaded = false
private[this] var variable: A = _
def apply() = { if (!loaded) { loaded = true; variable = initial }; variable }
def update(a: A) { loaded = true; variable = a }
}
где то (к сожалению) придется использовать ()
на каждый читать и писать.
scala> val lv = new LazyVar({ println("Hi!"); 5 })
lv: LazyVar[Int] = [email protected]
scala> lv()
Hi!
res2: Int = 5
scala> lv() = 7
scala> lv()
res4: Int = 7
Затем вы используете экземпляр этого класса вместо фактического var
и пройти через ленивый инициализатор. (lazy val
очень похож на это под капотом, компилятор просто защищает вас замечать.)
Наконец, если вы хотите иметь полностью функциональный объект, который иногда отсутствующее значение, var x: Option[X]
является конструкцией вы хотите использование; если вы не можете найти способ создания стандартных шаблонов Java (и вы не хотите попробовать что-то более экзотическое, как объекты, которые создают друг друга с большей и большей информацией, либо потому, что производительность критическая, и вы не можете себе это позволить , или вам не нравится писать много шаблонов, чтобы проверить тип, чтобы убедиться, что ваш объект правильно создан), но вы в противном случае хотите его использовать, var x: X = null
- это то, что я бы выбрал, а не _
. Если X
является примитивным, вам, вероятно, нужно будет правильно выбрать правильное значение (например, Double.NaN
вместо 0.0
, -1
, а не 0
для Int
), чтобы указать I-am-not-initialized. Если это общий код, и вы хотите, чтобы Any
вместо AnyRef
, asInstanceOf
-от взад и вперед между Any
и AnyRef
, вероятно, лучший выход из плохо типизированной ситуации (если вы действительно не можете использовать Option
, что в этот момент много понятнее).
Спасибо Rex. Мне нравится поздняя отсрочка инициализации. В моем случае мне нужно инициализировать переменную. Можно ли использовать ленивую инициализацию каким-то другим способом? – Dan
@ Дан - вид; Я исправил свой ответ. –