最终的val会增加对象的大小吗?

fre*_*low 7 scala final constants compile-time-constant companion-object

class Foo {
  final val pi = 3
}
Run Code Online (Sandbox Code Playgroud)

每个Foo对象都有pi会员吗?我应该放入pi伴侣物体吗?

axe*_*l22 4

如果您担心内存占用,您可以考虑将此字段移动到伴生对象中。

是的,类的每个实例Foo都会有该pi值——Scala 编译器不会消除这个声明。JVM 反射允许您删除类成员上的最终修饰符,并且Unsafe对象甚至允许修改这些修饰符。因此,Scala 编译器可以通过删除此字段来生成具有令人惊讶结果的代码,因此不应用此优化。

...
  minor version: 0
  major version: 50
  flags: ACC_PUBLIC, ACC_SUPER
...
{
  private final int pi;
    flags: ACC_PRIVATE, ACC_FINAL


  public final int pi();
    flags: ACC_PUBLIC, ACC_FINAL
    LineNumberTable:
      line 243: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
...
Run Code Online (Sandbox Code Playgroud)

事实上,一些编译器转换(例如专门化)甚至可能会删除底层成员的 Final 修饰符,因此finalScala 代码中感觉的东西可能不在final字节码级别上。

这:

class Foo[@specialized T] {
  final val pi: T = null.asInstanceOf[T]
}
Run Code Online (Sandbox Code Playgroud)

变成:

  ...
  public final T pi;
    flags: ACC_PUBLIC, ACC_FINAL
    Signature: #9                           // TT;


  public T pi();
    flags: ACC_PUBLIC
    LineNumberTable:
      line 243: 0
   ...
Run Code Online (Sandbox Code Playgroud)

上面,pi访问器方法(即它的 getter)不再是最终的。

Oracle JVM 中的 JIT 也不会在运行时从内存中的对象表示中删除该成员 - Foo32 位 JVM 上对象的运行时大小将为 16 字节(8 字节对象头 + 4 字节整数字段,四舍五入到 8 字节边界)。然而,JIT 可能决定将最终字段中的常量值内联到代码的某些部分中,以便消除某些字段写入。