Как члены объекта, после инициализации, val
s никогда не изменяют свои значения в течение времени жизни объекта. Таким образом, их значения гарантированно будут видны всем потокам, при условии, что ссылка на объект не скрылась в конструкторе. И фактически они получают модификаторы Java final
, как показано ниже:
object Obj {
val r = 1
def foo {
val a = 1
def bar = a
bar
}
}
Использование javap:
...
private final int r;
...
public void foo();
...
0: iconst_1
1: istore_1
2: aload_0
3: iload_1
4: invokespecial #31; //Method bar$1:(I)I
7: pop
...
private final int bar$1(int);
...
0: iload_1
1: ireturn
...
Как локальные методы, они используются только внутри метода или передаются во вложенный метод или замыкание в качестве аргументов (см. Выше bar$1
). Закрытие может быть передано другому потоку, но оно будет иметь только конечное поле со значением локального val
. Поэтому они видны с той точки, где они созданы для всех других потоков, и синхронизация не требуется.
Обратите внимание, что это ничего не говорит об объекте, на который указывает val
- он сам может быть изменяемым и требовать синхронизации.
В большинстве случаев вышеупомянутое не может быть нарушено путем отражения - объявление члена Scala val
фактически генерирует получатель с тем же именем и частным полем, к которому получает доступ получатель. Попытка использовать отражение для изменения поля приведет к NoSuchFieldException
. Единственный способ изменить его - добавить аннотацию specialized
в ваш класс, которая сделает специальные поля защищенными и, следовательно, доступными для отражения. В настоящее время я не могу думать ни о какой другой ситуации, которая могла бы изменить что-то, объявленное как val
...