Это правда, что вы могли бы определить ленивым значение, например, как это:
object Lazy {
def apply[A](init: => A): Lazy[A] = new Lazy[A] {
private var value = null.asInstanceOf[A]
@volatile private var initialized = false
override def toString =
if (initialized) value.toString else "<lazy>@" + hashCode.toHexString
def apply(): A = {
if (!initialized) this.synchronized {
if (!initialized) {
value = init
initialized = true
}
}
value
}
}
implicit def unwrap[A](l: Lazy[A]): A = l()
}
trait Lazy[+A] { def apply(): A }
Использование:
val x = Lazy {
println("aqui")
42
}
def test(i: Int) = i * i
test(x)
С другой стороны, имея lazy
в качестве модификатора языка при условии имеет то преимущество, что позволяет ему участвовать в принципе равномерного доступа . Я попытался найти для него запись в блоге, но нет ничего, что выходит за рамки getters и seters. Этот принцип на самом деле более фундаментален. Для значений, следующие унифицированы: val
, lazy val
, def
, var
, object
:
trait Foo[A] {
def bar: A
}
class FooVal[A](val bar: A) extends Foo[A]
class FooLazyVal[A](init: => A) extends Foo[A] {
lazy val bar: A = init
}
class FooVar[A](var bar: A) extends Foo[A]
class FooProxy[A](peer: Foo[A]) extends Foo[A] {
def bar: A = peer.bar
}
trait Bar {
def baz: Int
}
class FooObject extends Foo[Bar] {
object bar extends Bar {
val baz = 42
}
}
Ленивые значения были введены в Scala 2.6. Существует Lambda the Ultimate comment что свидетельствует о том, что рассуждения, возможно, придется делать с формализацией возможности иметь циклические ссылки:
Циклических зависимости требует связывания с ленивыми значениями. Предельные значения также могут использоваться для обеспечения выполнения этой инициализации компонента в порядке зависимости. Компонентный порядок отключения, к сожалению, должен быть закодирован вручную
Я не знаю, почему циклические ссылки не могут быть автоматически обрабатывается компилятором; возможно, были причины сложности или эффективности. A blog post by Iulian Dragos подтверждает некоторые из этих предположений.
Интересно, можно ли реализовать @lazy с помощью макрокоманд. –
Я не знаю, возможно ли это, но это было бы полезно. Существует множество различных вариантов использования ленивых валов. Например, если вы просто используете lazy val для кэширования чистой функции по соображениям производительности, вам все равно, будет ли значение вычисляться дважды в раскадном сценарии. Таким образом, вы можете отключить синхронизацию. Но если вы используете ленивый вал для управления внешним ресурсом, вам все равно. Очевидно, что поведение по умолчанию должно быть максимально безопасным, но некоторые настройки были бы очень хорошими ... –
Маркировка переменной как ленивой с ключевым словом имеет дополнительное преимущество в том, что она может изменить поведение существующего заявления, не касаясь тела , Изменяя только одно ключевое слово, другие объекты не обращают внимания, и тело остается нетронутым. В других реализациях вам всегда нужно добавить какой-нибудь Lazy-блок или неявное определение в правильной области. Замечание Макроса звучит очень интересно, предлагая разные вкусы ленивого, может быть, это хорошая вещь. Хорошо, что @Eugene здесь :) –