Scala: может ли компилятор эффективно оптимизировать константы?

Давайте рассмотрим следующее:

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, чтобы не создавать экземпляр BUFFER_SIZE для каждого экземпляра Foo, чтобы тратить время и пространство? Или стоит пойти с первым?

2 ответа

Решение

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 становится методом получения. Теперь давайте посмотрим на фактический байт-код:

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 

который будет произведен, если вы пометите BUFFER_SIZE с final модификатор:

class Foo {
  final val BUFFER_SIZE = 1024

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

Префикс поля с private[this] как сказал @ghik, тоже поможет. Разница в том, что final производит геттер с тривиальным кодом, тогда как private[this] прямо указывает на значение.

Он никогда не будет оптимизирован таким образом, потому что это член, который должен быть доступен извне (например, из Java).

Единственная ситуация, когда он может быть оптимизирован (в будущем), это когда он объявлен как private[this]:

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

В любом случае, я бы сказал, что лучше всего использовать объект-компаньон.

Другие вопросы по тегам