4

让我们考虑以下几点:

object Foo {
  val BUFFER_SIZE = 1024
}

class Foo {
  .
  .
  .

  val buffer = new Array[Byte](Foo.BUFFER_SIZE)

这太冗长了,与 Java 的静态(最终)变量相比似乎并不优雅,尤其是因为常量的定义和用法相距太远,无法立即理解代码。我想要的是这样的:

class Foo {
  val BUFFER_SIZE = 1024

  val buffer = new Array[Byte](BUFFER_SIZE)

问题是,Scala 编译器是否足够聪明,不会为每个 Foo 实例实例化 BUFFER_SIZE 以浪费时间和空间?还是应该选择第一个?

4

2 回答 2

6

TLDR:不,它不是很好,但你可以指导编译器。

而且很容易检查(我把代码放到了test.scala中):

scalac test.scala 
javap Foo.class
// Compiled from "test.scala"
// public class Foo {
//   public int BUFFER_SIZE();
//   public byte[] buffer();
//   public Foo();
// }

所以 val 最终成为一个 getter 方法。现在让我们看看实际的字节码:

javap -c Foo.class
Compiled from "test.scala"
public class Foo {
  public int BUFFER_SIZE();
    Code:
       0: aload_0       
       1: getfield      #15                 // Field BUFFER_SIZE:I
       4: ireturn       

  // .... irrelevant parts

正如您所看到的,有一个getfield代码意味着每个类实例都会有不同的实例(而不是getstatic意味着访问静态变量)。高度优化的代码看起来像

public final int BUFFER_SIZE();
    Code:
       0: sipush        1024
       3: ireturn 

final如果你用修饰符标记 BUFFER_SIZE 将会产生:

class Foo {
  final val BUFFER_SIZE = 1024

  val buffer = new Array[Byte](BUFFER_SIZE)
}

private[this]使用@ghik 所说的前缀字段也可以解决问题。不同之处在于final生成 getter 的代码很简单,而private[this]直接内联值。

于 2013-10-04T20:22:14.650 回答
2

它永远不会像这样被优化出来,因为它是一个必须从外部可用的成员(例如来自Java)。

它可能被优化的唯一情况(将来)是它被声明为private[this]

class Foo {
  private[this] val BUFFER_SIZE = 1024
  val buffer = new Array[Byte](BUFFER_SIZE)
}

无论如何,我仍然会说要走的路是使用伴生对象。

于 2013-10-04T20:21:11.397 回答