Как в Scala 2.10 реализованы переменные класса lazy val?

Этот ответ на Что такое ( hidden) стоимость ленивого val в Scala? показывает, как они были реализованы в Scala 2.7. Но, как говорится в комментариях, это должно было измениться с тех пор, поэтому мне любопытно, какова текущая (2.10) реализация переменных класса lazy val?


person Petr    schedule 14.07.2013    source источник


Ответы (2)


Скомпилировано с помощью scala 2.10.2:

class Foo {

  lazy val bar = math.pow(5, 3)

}

Затем декомпилировал результат с помощью JD-GUI:

import scala.math.package.;
import scala.reflect.ScalaSignature;

@ScalaSignature(bytes="\006\001e1A!\001\002\001\013\t\031ai\\8\013\003\r\tq\001P3naRLhh\001\001\024\005\0011\001CA\004\013\033\005A!\"A\005\002\013M\034\027\r\\1\n\005-A!AB!osJ+g\rC\003\016\001\021\005a\"\001\004=S:LGO\020\013\002\037A\021\001\003A\007\002\005!A!\003\001EC\002\023\0051#A\002cCJ,\022\001\006\t\003\017UI!A\006\005\003\r\021{WO\0317f\021!A\002\001#A!B\023!\022\001\0022be\002\002")
public class Foo {

    private double bar;
    private volatile boolean bitmap$0;

    private double bar$lzycompute() {
        synchronized (this) { 
            if (!this.bitmap$0) { 
                this.bar = package..MODULE$.pow(5.0D, 3.0D); 
                this.bitmap$0 = true; 
            } 
            return this.bar; 
        }  
    } 

    public double bar() { 
        return this.bitmap$0 ? this.bar : bar$lzycompute(); 
    }

}

Изменить. Вот как выглядят три поля:

class Foo {

  lazy val a = math.pow(5, 1)
  lazy val b = math.pow(5, 2)
  lazy val c = math.pow(5, 3)

}

Декомпилировано:

import scala.math.package.;
import scala.reflect.ScalaSignature;

@ScalaSignature(bytes="\006\001\0052A!\001\002\001\013\t\031ai\\8\013\003\r\tq\001P3naRLhh\001\001\024\005\0011\001CA\004\013\033\005A!\"A\005\002\013M\034\027\r\\1\n\005-A!AB!osJ+g\rC\003\016\001\021\005a\"\001\004=S:LGO\020\013\002\037A\021\001\003A\007\002\005!A!\003\001EC\002\023\0051#A\001b+\005!\002CA\004\026\023\t1\002B\001\004E_V\024G.\032\005\t1\001A\t\021)Q\005)\005\021\021\r\t\005\t5\001A)\031!C\001'\005\t!\r\003\005\035\001!\005\t\025)\003\025\003\t\021\007\005\003\005\037\001!\025\r\021\"\001\024\003\005\031\007\002\003\021\001\021\003\005\013\025\002\013\002\005\r\004\003")
public class Foo {

    private double a;
    private double b;
    private double c;

    private volatile byte bitmap$0;

    private double a$lzycompute() {
        synchronized (this) {
            if ((byte)(this.bitmap$0 & 0x1) == 0) {
                this.a = package..MODULE$.pow(5.0D, 1.0D); 
                this.bitmap$0 = ((byte)(this.bitmap$0 | 0x1)); 
            } 
            return this.a;
        }  
    } 

    private double b$lzycompute() { 
        synchronized (this) {
            if ((byte)(this.bitmap$0 & 0x2) == 0) {
                this.b = package..MODULE$.pow(5.0D, 2.0D); 
                this.bitmap$0 = ((byte)(this.bitmap$0 | 0x2)); 
            } 
            return this.b; 
        }  
    } 

    private double c$lzycompute() { 
        synchronized (this) {
            if ((byte)(this.bitmap$0 & 0x4) == 0) {
                this.c = package..MODULE$.pow(5.0D, 3.0D); 
                this.bitmap$0 = ((byte)(this.bitmap$0 | 0x4)); 
            } 
            return this.c;
        }
    }

    public double a() {
        return (byte)(this.bitmap$0 & 0x1) == 0 ? a$lzycompute() : this.a;
    }

    public double b() { 
        return (byte)(this.bitmap$0 & 0x2) == 0 ? b$lzycompute() : this.b; 
    } 

    public double c() { 
        return (byte)(this.bitmap$0 & 0x4) == 0 ? c$lzycompute() : this.c;
    }

}
person Chris Martin    schedule 14.07.2013
comment
Спасибо, поэтому он использует фиксированную блокировку с двойной проверкой, доступную в Java 5. . - person Petr; 14.07.2013
comment
Если есть несколько lazy переменных, существует ли отдельный логический флаг для каждой из них? Или в таком случае Scala использует какие-то битовые флаги? - person Petr; 14.07.2013
comment
Обновил ответ другим примером, содержащим три ленивых переменных. Если вы хотите знать, что происходит, когда он превышает байт, вы можете попробовать сами;) - person Chris Martin; 14.07.2013
comment
О, и спасибо, что определили это как блокировку с двойной проверкой. Я кое-что узнал сегодня. - person Chris Martin; 15.07.2013
comment
@bwawok Первая проверка за a() перед блокировкой. Вторая проверка - через a$lzycompute() после блокировки. - person Chris Martin; 16.07.2013
comment
@bwawok Насколько я могу судить, код, который мы видим выше, синхронизируется только тогда, когда значение еще не вычислено. Флаг непостоянен, поэтому нет необходимости в синхронизированном блоке. - person Machisuji; 16.07.2013

Обновление Scala 2.12.1 (декабрь 2016 г., три года спустя).

После PR 5294 (фаза полей полностью расширяет ленивые значения и модули), вы можете прочитать в совершить 743f0d2:

Ленивый вал без местных.

Теперь synchronized стирается специально, чтобы избежать боксов, мы можем отказаться от этой работы.

Обратите внимание, что это добавляет дополнительное приведение и вызов геттера на медленном пути, но это, вероятно, не имеет значения.

class C { def foo = {lazy val x = {println("a"); "A" }; x } }

становится:

def foo(): String = {
  lazy <artifact> val x$lzy: scala.runtime.LazyRef[String] = new scala.runtime.LazyRef[String]();

  <artifact> private def x$lzycompute(): String =
    x$lzy.synchronized[String]{
      if (x$lzy.initialized())
        x$lzy.value() // NOTE: gets an `.asInstanceOf[String]` after erasure
      else
        {
          x$lzy.value_=({
            scala.Predef.println("a");
            "A"
          });
          x$lzy.initialized_=(true);
          x$lzy.value() // NOTE: gets an `.asInstanceOf[String]` after erasure
        }
    }

  lazy def x(): String =
    if (x$lzy.initialized())
      x$lzy.value() // NOTE: gets an `.asInstanceOf[String]` after erasure
    else
      x$lzycompute();

  x()
}
person VonC    schedule 26.12.2016