Одна удобная функция Scala lazy val
, где оценка a val
отложен, пока это не необходимо (в первом доступе).
Конечно, a lazy val
должен иметь немного служебные - где-нибудь, Scala должен отслеживать то, было ли значение уже оценено, и оценка должна синхронизироваться, потому что несколько потоков могли бы попытаться получить доступ к значению впервые одновременно.
Что точно является стоимостью a lazy val
- есть ли скрытый булев флаг, связанный с a lazy val
отслеживать, если это было оценено или нет, что точно синхронизируется и там еще затраты?
Кроме того, предположите, что я делаю это:
class Something {
lazy val (x, y) = { ... }
}
Это то же как наличие двух отдельных lazy val
s x
и y
или сделайте я получаю издержки только однажды для пары (x, y)
?
Это взято из списка рассылки scala и дает подробности реализации lazy
с точки зрения кода Java (а не байт-кода):
class LazyTest {
lazy val msg = "Lazy"
}
скомпилирован во что-то эквивалентное следующий код Java:
class LazyTest {
public int bitmap$0;
private String msg;
public String msg() {
if ((bitmap$0 & 1) == 0) {
synchronized (this) {
if ((bitmap$0 & 1) == 0) {
synchronized (this) {
msg = "Lazy";
}
}
bitmap$0 = bitmap$0 | 1;
}
}
return msg;
}
}
Похоже, что компилятор организует для поля int растрового изображения уровня класса пометку нескольких ленивых полей как инициализированных (или нет) и инициализирует целевое поле в синхронизированном блоке, если соответствующий xor растровое изображение указывает, что это необходимо.
Использование:
class Something {
lazy val foo = getFoo
def getFoo = "foo!"
}
дает образец байт-кода:
0 aload_0 [this]
1 getfield blevins.example.Something.bitmap$0 : int [15]
4 iconst_1
5 iand
6 iconst_0
7 if_icmpne 48
10 aload_0 [this]
11 dup
12 astore_1
13 monitorenter
14 aload_0 [this]
15 getfield blevins.example.Something.bitmap$0 : int [15]
18 iconst_1
19 iand
20 iconst_0
21 if_icmpne 42
24 aload_0 [this]
25 aload_0 [this]
26 invokevirtual blevins.example.Something.getFoo() : java.lang.String [18]
29 putfield blevins.example.Something.foo : java.lang.String [20]
32 aload_0 [this]
33 aload_0 [this]
34 getfield blevins.example.Something.bitmap$0 : int [15]
37 iconst_1
38 ior
39 putfield blevins.example.Something.bitmap$0 : int [15]
42 getstatic scala.runtime.BoxedUnit.UNIT : scala.runtime.BoxedUnit [26]
45 pop
46 aload_1
47 monitorexit
48 aload_0 [this]
49 getfield blevins.example.Something.foo : java.lang.String [20]
52 areturn
53 aload_1
54 monitorexit
55 athrow
Значения, инициализированные в кортежах типа lazy val (x, y) = {...}
, имеют вложенное кэширование с помощью того же механизма. Результат кортежа лениво оценивается и кешируется, а доступ к x или y запускает оценку кортежа. Извлечение отдельного значения из кортежа выполняется независимо и лениво (и кэшируется). Таким образом, приведенный выше код с двойным экземпляром генерирует поле x
, y
и поле x $ 1
типа Tuple2
.