102

我曾经认为private val并且private final val是相同的,直到我在 Scala Reference 中看到第 4.1 节:

常量值定义的形式为

final val x = e

其中 e 是一个常量表达式(第 6.24 节)。final 修饰符必须存在,并且不能给出类型注释。对常量值 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 valis not private final val.

为什么scalac不只是对待private valas private final val?有什么根本原因吗?

4

2 回答 2

84

所以,这只是一个猜测,但在 Java 中一直存在一个烦恼,即右侧带有文字的最终静态变量作为常量内联到字节码中。这肯定会带来性能优势,但如果“常量”发生变化,它会导致定义的二进制兼容性中断。在定义最终的静态变量时,其值可能需要更改,Java 程序员不得不求助于使用方法或构造函数初始化值等技巧。

Scala 中的 val 在 Java 意义上已经是最终的。看起来 Scala 的设计者正在使用冗余修饰符 final 来表示“允许内联常量值”。因此 Scala 程序员可以完全控制这种行为而无需求助于黑客:如果他们想要一个内联常量,一个永远不会改变但速度很快的值,他们会写“final val”。如果他们想要在不破坏二进制兼容性的情况下灵活地更改值,只需“val”。

于 2012-11-16T08:27:22.723 回答
8

我认为这里的混淆源于将不变性与 final 的语义混为一谈。 vals 可以在子类中被覆盖,因此除非明确标记,否则不能被视为 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
于 2012-11-17T06:38:43.143 回答