Ответ на другой вопрос вводит в заблуждение. Существует два значения термина final
: а) для полей / методов Scala и методов Java это означает, что «нельзя переопределить в подклассе» и б) для полей Java, а в байт-коде JVM это означает, что поле должно быть инициализировано конструктор и не может быть переназначен ".
Параметры класса, отмеченные val
(или, что то же самое, параметры класса case без модификатора) действительно являются окончательными во втором смысле и, следовательно, потокобезопасными.
Вот доказательство:
scala> class A(val a: Any); class B(final val b: Any); class C(var c: Any)
defined class A
defined class B
defined class C
scala> import java.lang.reflect._
import java.lang.reflect._
scala> def isFinal(cls: Class[_], fieldName: String) = {
| val f = cls.getDeclaredFields.find(_.getName == fieldName).get
| val mods = f.getModifiers
| Modifier.isFinal(mods)
| }
isFinal: (cls: Class[_], fieldName: String)Boolean
scala> isFinal(classOf[A], "a")
res32: Boolean = true
scala> isFinal(classOf[B], "b")
res33: Boolean = true
scala> isFinal(classOf[C], "c")
res34: Boolean = false
Или с помощью javap
, который можно удобно запустить из REPL:
scala> class A(val a: Any)
defined class A
scala> :javap -private A
Compiled from "<console>"
public class A extends java.lang.Object implements scala.ScalaObject{
private final java.lang.Object a;
public java.lang.Object a();
public A(java.lang.Object);
}