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

11 浏览
0 Comments

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

我曾经认为private valprivate final val是一样的,直到我看到了《Scala参考》的第4.1节:\n

\n常量值定义的形式为\n

final val x = e

\n其中e是一个常量表达式(§6.24)。必须有final修饰符,并且不能给出类型注解。对常量值x的引用本身被视为常量表达式;在生成的代码中,它们被定义的右侧e替换。\n

\n我写了一个测试:\n

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

\njavap -c输出:\n

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."":()V
       4: aload_0       
       5: iconst_0      
       6: putfield      #14                 // Field privateVal:I
       9: return
}

\n字节码正如《Scala参考》所说:private val并不等同于private final val。\n为什么scalac不把private val当作private final val来处理呢?是否有任何根本原因?

0
0 Comments

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

混淆不可变性和final的语义是导致混淆的原因。`val`可以在子类中被重写,因此除非显式标记为final,否则不能被视为final。

REPL在行级别提供类作用域。参见以下示例:

scala> $iw.getClass.getPackage
res0: Package = package $line3
scala> private val x = 5
:5: error: value x cannot be accessed in object $iw
  lazy val $result = `x`
scala> private val x = 5; println(x);
5

我在谈论`private val`。它可以被重写吗?

不,私有的val不能被重写。你可以在子类中重新定义另一个同名的私有val,但它只是一个完全不同的val,只是碰巧有相同的名称。(对旧val的所有引用仍然引用旧val)

然而,似乎不仅仅是重写行为,因为我可以在解释器中在没有类上下文的情况下创建一个final val(甚至是final var)。

问题的解决方法是使用`private final val`来确保val不能被子类重写。这样可以避免混淆,并确保代码的可预测性和可维护性。

0
0 Comments

在Java中,final static变量的右侧如果是一个字面值,会被内联为字节码常量。这样做确实会带来性能上的好处,但如果这个"常量"发生变化,就会导致定义的二进制兼容性被破坏。因此,在Java中,当定义的final static变量的值可能需要改变时,程序员必须采取一些hack的方法,比如使用方法或构造函数来初始化值。

在Scala中,val已经在Java的意义上是final的。Scala的设计者使用冗余修饰符final来表示"内联常量值的权限"。因此,Scala程序员可以完全控制这种行为,而无需使用hack的方法:如果他们希望有一个内联常量,即一个永远不会改变但是速度很快的值,他们写"final val"。如果他们希望在不破坏二进制兼容性的情况下更改值,只需写"val"即可。

是的,这是非私有val的原因,但是私有val显然无法在其他类中内联,并以相同的方式破坏兼容性。

private val更改为private final val是否会产生任何二进制兼容性问题?

- Waldman 请问,你在第二段中的意思是val吗?

关于Java中final static变量的二进制兼容性,可以参考这里的详细信息:docs.oracle.com/javase/specs/jls/se7/html/…

0