在Java中,当跨多个线程(通常)使用对象时,最好将字段设为final。例如,
public class ShareMe {
private final MyObject obj;
public ShareMe(MyObject obj) {
this.obj = obj;
}
}
在这种情况下,obj的可见性将在多个线程中保持一致(假设obj具有所有最终字段),因为它是使用final关键字安全构造的。
在scala中,似乎val不会编译为最终引用,而是val是scala中的语义,阻止您重新分配变量(Scala final variables in constructor)。如果scala构造函数变量未定义为final,它们是否会遇到同样的问题(在actor中使用这些对象时)?
答案 0 :(得分:49)
另一个问题的答案是误导性的。术语final
有两个含义:a)对于Scala字段/方法和Java方法,它意味着“不能在子类中重写”和b)对于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);
}
答案 1 :(得分:1)
我想我可能误解了var是如何编译的。我创建了示例类
class AVarTest(name:String) {
def printName() {
println(name)
}
}
我跑了javap -private
,结果是
public class AVarTest extends java.lang.Object implements scala.ScalaObject{
private final java.lang.String name;
public void printName();
public AVarTest(java.lang.String);
}
名称实际上已编译为final。
这也显示在Scala val has to be guarded with synchronized for concurrent access?
中