Scala的final和val用于并发可见性



在Java中,当跨多个线程(以及一般情况下)使用对象时,将字段设置为final是一种很好的做法。例如,

public class ShareMe {
    private final MyObject obj;
    public ShareMe(MyObject obj) {
        this.obj = obj;
    }
}

在这种情况下,obj的可见性在多个线程中是一致的(让我们假设obj也有所有final字段),因为它是使用final关键字安全地构造的。

在scala中,val似乎并没有被编译为最终引用,而是在scala中,val是一种语义,它可以防止你重新分配变量(构造函数中的scala最终变量)。如果scala构造函数变量没有定义为final,它们是否会遇到同样的问题(当在actor中使用这些对象时)?

另一个问题的答案是误导性的。术语final有两个含义:a)对于Scala字段/方法和Java方法,它意味着"不能在子类中重写";b)对于Java字段和JVM字节码,它意味着"字段必须在构造函数中初始化,不能重新分配"。

val标记的类参数(或者,等价地,没有修饰符的大小写类参数)在第二种意义上确实是final的,因此是线程安全的。

证明:

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);
}

我想我可能误解了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);
}

name实际上被编译为final。

这也显示在Scala val必须被保护与同步的并发访问?

最新更新