为什么`private val`和`private final val`不同?

时间:2012-11-16 07:43:07

标签: scala private final

我曾经认为private valprivate final val是相同的,直到我在Scala参考中看到4.1节:

  

常量值定义的格式为

final val x = e
     

其中e是常数表达式(§6.24)。必须存在最终修饰符,并且不能给出类型注释。对常量值x的引用本身被视为常量表达式;在生成的代码中,它们被定义的右侧e。

取代

我写了一个测试:

class PrivateVal {
  private val privateVal = 0
  def testPrivateVal = privateVal
  private final val privateFinalVal = 1
  def testPrivateFinalVal = privateFinalVal
}

javap -c输出:

Compiled from "PrivateVal.scala"
public class PrivateVal {
  public int testPrivateVal();
    Code:
       0: aload_0       
       1: invokespecial #19                 // Method privateVal:()I
       4: ireturn       

  public int testPrivateFinalVal();
    Code:
       0: iconst_1      
       1: ireturn       

  public PrivateVal();
    Code:
       0: aload_0       
       1: invokespecial #24                 // Method java/lang/Object."<init>":()V
       4: aload_0       
       5: iconst_0      
       6: putfield      #14                 // Field privateVal:I
       9: return
}

字节代码就像Scala Reference所说:private val不是private final val

scalac 为什么不将private val视为private final val?有没有潜在的原因?

2 个答案:

答案 0 :(得分:80)

所以,这只是一个猜测,但在Java中长期存在的烦恼是,右侧的文字的最终静态变量被内联到字节码中作为常量。这确实产生了性能优势,但是如果&#34;常量&#34;它会导致定义的二进制兼容性中断。永远改变了。在定义其值可能需要更改的最终静态变量时,Java程序员不得不求助于使用方法或构造函数初始化值的黑客攻击。

Scala中的val已经是Java意义上的最终版本。看起来Scala的设计师正在使用冗余修饰符final来表示&#34;允许内联常量值&#34;。所以Scala程序员可以完全控制这种行为,而不需要求助于黑客攻击:如果他们想要一个内联常量,一个永远不会改变但速度很快的值,他们就会编写&#34; final val&#34;。如果他们想要灵活地改变价值而不破坏二进制兼容性,那么只需要&#34; val&#34;。

答案 1 :(得分:7)

我认为这里的困惑源于将不变性与最终语义混为一谈。 val可以在子类中被覆盖,因此除非明确标记,否则不能将其视为final。

@Brian REPL在行级别提供类范围。参见:

scala> $iw.getClass.getPackage
res0: Package = package $line3

scala> private val x = 5
<console>:5: error: value x cannot be accessed in object $iw
  lazy val $result = `x`

scala> private val x = 5; println(x);
5