Scala final variables in constructor

2019-04-30 09:59发布

I'm still pretty new to Scala, but I know you can define class variables that are initialized in the constructor like

class AClass(aVal: String)

which would be like doing the following in java

class AClass {
    private String aVal;

    public AClass(String aVal) {
        this.aVal = aVal;
    }
}

In Java, I would declare aVal as final. Is there a way to make the aVal variable final in the Scala syntax?

EDIT: Here is what I am seeing when I compile the following Scala class:

class AClass(aVal: String) {
  def printVal() {
    println(aVal)
  }
}

I ran javap -private and got the output

public class AClass extends java.lang.Object implements scala.ScalaObject{
  private final java.lang.String aVal;
  public void printVal();
  public AClass(java.lang.String);
}

When I change the scala class definition to have class AClass(**val** aVal: String) I get the following output from javap -private

public class AClass extends java.lang.Object implements scala.ScalaObject{
  private final java.lang.String aVal;
  public java.lang.String aVal();
  public void printVal();
  public AClass(java.lang.String);
}

The public method aVal is generated. I'm still learning here - can anyone explain why that is generated?

Note I am using scala 2.9

2条回答
祖国的老花朵
2楼-- · 2019-04-30 10:48

Copied from my answer: Scala final vs val for concurrency visibility

There are two meanings of the term final: a) for Scala fields/methods and Java methods it means "cannot be overridded in a subclass" and b) for Java fields and in JVM bytecode it means "the field must be initialized in the constructor and cannot be reassigned".

Class parameters marked with val (or, equivalently, case class parameters without a modifier) are indeed final in second sense, and hence thread safe.

Here's proof:

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

Or with javap, which can be conveniently run from the 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);
}

If the class parameter of a non-case class is not marked as a val or var, and it is not referred to from any methods, it need only be visible to the constructor of the class. The Scala compiler is then free to optimize the field away from the bytecode. In Scala 2.9.1, this appears to work:

scala> class A(a: Any)
defined class A

scala> :javap -private A
Compiled from "<console>"
public class A extends java.lang.Object implements scala.ScalaObject{
    public A(java.lang.Object);
}
查看更多
时光不老,我们不散
3楼-- · 2019-04-30 10:49
class AClass(aVal: String)

In this code, aVal is a final variable. So You already have a final variable.

class AClass(val aVal: String)

In this code, aVal is final and you have getter of aVAl. So you can use it like below

scala> val a= new AClass("aa")
a: A1 = A1@1d7d58f

scala> a.aVal
res2: String = aa

And finally,

class AClass(var aVal: String)

In this code, aVal is not final and you have getter and setter of aVal. So you can use it like below

scala> val a= new AClass("aa")
a: AClass = AClass@1c059f6

scala> a.aVal
res3: String = aa

scala> a.aVal = "bb"
a.aVal: String = bb

scala> a.aVal
res4: String = bb
查看更多
登录 后发表回答