一、lazy关键字简介

lazy是scala中用来实现惰性赋值的关键字,被lazy修饰的变量初始化的时机是在第一次使用此变量的时候才会赋值,并且仅在第一次调用时计算值,即值只会被计算一次,赋值一次,再之后不会被更改了,这个特性有点熟悉哎?没错,所以lazy修饰的变量必须同时是val修饰的不可变变量。

下面是一个惰性赋值的例子:

package cc11001100.scala.lazyStudy

class FooBar() {

  println("foo before")
lazy val foo: String = initFoo()
println("foo after") println("bar before")
val bar: String = initBar()
println("bar after") def initFoo(): String = {
println("initFoo exec")
"foo"
} def initBar(): String = {
println("initBar exec")
"bar"
} } object LazyStudy { def main(args: Array[String]): Unit = { val foobar = new FooBar() println(foobar.foo)
println(foobar.foo) println(foobar.bar)
println(foobar.bar) } }

输出:

foo before
foo after
bar before
initBar exec
bar after
initFoo exec
foo
foo
bar
bar

需要注意的是lazy修饰的变量后面只需要是个表达式就可以,一般是调用个方法计算值,也可以是字面值常量,但字面值常量的话又有什么意义呢?

二、原理探究

scala也是编译成字节码跑在jvm上的,而jvm的字节码指令并没有提供对lazy这种语义的支持,所以由此可以推断,lazy只是一个语法糖,scala编译器在编译时期对其做一些包装转换,但究竟是如何转换的呢,可以写一段代码编译然后反编译看一下。

编写一段scala代码,有两个变量,一个使用lazy修饰,一个不使用lazy修饰:

package cc11001100.scala.lazyStudy

class LazyInitDemoForDecompilation {
lazy val foo = "foo"
val bar = "bar"
} object LazyInitDemoForDecompilation { def main(args: Array[String]): Unit = {
val o = new LazyInitDemoForDecompilation()
println(o.foo)
println(o.bar)
} }

然后编译为字节码文件,再使用jd-gui等工具将其反编译:

package cc11001100.scala.lazyStudy;

import scala.reflect.ScalaSignature;

@ScalaSignature(bytes="\006\001}2A!\003\006\001#!)q\003\001C\0011!A1\004\001EC\002\023\005A\004C\004&\001\t\007I\021\001\017\t\r\031\002\001\025!\003\036\017\0259#\002#\001)\r\025I!\002#\001*\021\0259b\001\"\001+\021\025Yc\001\"\001-\005qa\025M_=J]&$H)Z7p\r>\024H)Z2p[BLG.\031;j_:T!a\003\007\002\0231\f'0_*uk\022L(BA\007\017\003\025\0318-\0317b\025\005y\021AC2dcE\002\004'M\0311a\r\0011C\001\001\023!\t\031R#D\001\025\025\005i\021B\001\f\025\005\031\te.\037*fM\0061A(\0338jiz\"\022!\007\t\0035\001i\021AC\001\004M>|W#A\017\021\005y\031S\"A\020\013\005\001\n\023\001\0027b]\036T\021AI\001\005U\0064\030-\003\002%?\t11\013\036:j]\036\f1AY1s\003\021\021\027M\035\021\00291\013'0_%oSR$U-\\8G_J$UmY8na&d\027\r^5p]B\021!DB\n\003\rI!\022\001K\001\005[\006Lg\016\006\002.aA\0211CL\005\003_Q\021A!\0268ji\")\021\007\003a\001e\005!\021M]4t!\r\0312'N\005\003iQ\021Q!\021:sCf\004\"AN\037\017\005]Z\004C\001\035\025\033\005I$B\001\036\021\003\031a$o\\8u}%\021A\bF\001\007!J,G-\0324\n\005\021r$B\001\037\025\001")
public class LazyInitDemoForDecompilation
{
private String foo; private String foo$lzycompute()
{
// 因为在调用此方法之前已经判断过一次标志位的值了,
// 所以可以看做是一种被拆散了的DCL
synchronized (this)
{
if (!this.bitmap$0)
{
this.foo = "foo";
this.bitmap$0 = true;
}
}
return this.foo;
} public String foo()
{
// 每次获取foo的值的时候,先判断是否已经初始化过了,
// 如果还没有初始化就将其初始化,否则直接将已经计算出的值返回
return !this.bitmap$0 ? foo$lzycompute() : this.foo;
} public String bar()
{
return this.bar;
} // bar变量直接为其赋值的
private final String bar = "bar";
// 这个变量是一个标志位,用来记录foo变量是否已经被初始化过了
private volatile boolean bitmap$0; public static void main(String[] paramArrayOfString)
{
LazyInitDemoForDecompilation..MODULE$.main(paramArrayOfString);
}
}

在object中执行:

package cc11001100.scala.lazyStudy;

import scala.Predef.;

public final class LazyInitDemoForDecompilation$
{
public static MODULE$; static
{
new ();
} public void main(String[] args)
{
LazyInitDemoForDecompilation o = new LazyInitDemoForDecompilation();
// 会将对变量的访问替换成调用访问器,
// 这样的话编译器就可以很鸡贼的在访问器方法中插入各种处理以提供N多的语法糖,挺机智的
Predef..MODULE$.println(o.foo());
Predef..MODULE$.println(o.bar());
} private LazyInitDemoForDecompilation$()
{
MODULE$ = this;
}
}

综上源码,得出结论,scala的lazy关键字就是编译器在编译期将变量的初始化过程替换为Double Check Lock,类似于Java中的懒汉式单例模式初始化。

.

scala笔记之惰性赋值(lazy)的更多相关文章

  1. es6学习笔记2-解构赋值

    解构赋值基本概论就按照一定的模式通过数组或者对象对一组变量进行赋值的过程. 1.通过数组对变量进行赋值: /*通过这种方式赋值要注意左右两边的结构模式要一样,在赋值的时候,根据位置进行赋值对应模式.* ...

  2. 2018-12-09 疑似bug_中文代码示例之Programming in Scala笔记第九十章

    续前文: 中文代码示例之Programming in Scala笔记第七八章 源文档库: program-in-chinese/Programming_in_Scala_study_notes_zh ...

  3. 2018-11-27 中文代码示例之Programming in Scala笔记第七八章

    续前文: 中文代码示例之Programming in Scala学习笔记第二三章 中文代码示例之Programming in Scala笔记第四五六章. 同样仅节选有意思的例程部分作演示之用. 源文档 ...

  4. RabbitMQ 惰性队列Lazy Queue

    RabbitMQ 队列分为几种类型,按照不同维度来分,可以分为排他性队列.普通队列.延迟队列.惰性队列.发布订阅队列等. 今天我们讨论的主角是惰性队列 Lazy Queue.众所周知,队列可以存储消息 ...

  5. ES6-11学习笔记--解构赋值

    解构赋值:按照一定模式,从数组和对象中提取值,对变量进行赋值.   数组解构 对象解构 字符串解构 应用场景     曾经的赋值噩梦,非解构赋值数组: let arr = [1, 2, 3]; let ...

  6. Scala 笔记

    环境 1. Intellij Idea 2. Scala 插件 3. http://scala-lang.org/ 教程: idea官方教程: https://www.jetbrains.com/he ...

  7. Scala笔记整理

    使用类型参数化数组(Array) 创建java.math.BigInteger实例: var big = new java .math.BigInteget("12345678") ...

  8. es6学习笔记--解构赋值

    昨天学习了es6语法中解构赋值,解构赋值在声明中和函数传参提高了灵活性和便捷性,值得掌握该语法. 概念: ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构.   数组的解构 ...

  9. Programming In Scala笔记-第十七章、Scala中的集合类型

    本章主要介绍Scala中的集合类型,主要包括:Array, ListBuffer, Arraybuffer, Set, Map和Tuple. 一.序列 序列类型的对象中包含多个按顺序排列好的元素,可以 ...

随机推荐

  1. SQL手工注入漏洞测试(Sql Server数据库)

    还是先找到注入点,然后order by找出字段数:4 通过SQL语句中and 1=2 union select 1,2,3……,n联合查询,判断显示的是哪些字段,就是原本显示标题和内容时候的查询字段. ...

  2. tomcat运行JSP时产生的错误:”javax.servlet.servletexception: java.lang.nosuchmethoderror”

    这个错误其实是多次重复编译JAVA文件导致的,需要clean操作,简单的来说就是删除tomcat下work文件夹中工作空间,也可以理解为自己的虚拟路径,比如我运行的jsp的路径:localhost:8 ...

  3. NBPL团队总结

    我们团队钱多多记账软件项目从2017年12月号开始,历时两个周.这两个周,我们从头学起,学到了很多新的知识,对一些概念有了认知,关于团队协作,关于团队建设,关于Android开发.回顾前两周,我们一致 ...

  4. “数学口袋精灵”App的第一个Sprint计划

    一.现状 我们这个团队想制作一个关于运算的游戏类型手机软件,针对我们这个学期的Android软件开发的课程,制作出一个关于数学算术游戏软件. 二.任务认领 第一阶段先把静态网页制作出来,各自的任务: ...

  5. 从编译DotNetOpenAuth中学到的程序集强签名知识

    1. 背景 最近在研究DotNetOpenAuth——OAuth的一个.NET开源实现,官方网站:http://dotnetopenauth.net/ . 从GitHub签出DotNetOpenAut ...

  6. [转帖]QEMU和QEMU-KVM的关系

    QEMU和QEMU-KVM的关系 https://blog.csdn.net/shengxia1999/article/details/52244119   版权声明:本文为博主原创文章,未经博主允许 ...

  7. Java之Map的使用场景

    总结之 Map接口 的使用场景(day04) Map: Map中的集合,元素是成对存在的(理解为夫妻).每个元素由键与值两部分组成,通过键可以找对所对应的值 Map中的集合不能包含重复的键,值可以重复 ...

  8. Go匿名函数

    1.GO语言的匿名函数就是闭包 基本概念 闭包是可以包含自由(未绑定到特定对象)变量的代码块,这些变量不在这个代码块内或者任何全局上下文中定义,而是在定义代码块的环境中定义.要执行的代码块(由于自由变 ...

  9. 【Revit API】脱离中心文件

    话不多说,直接代码,整个过程不需要发起Transaction OpenOptions op = new OpenOptions(); op.Audit = true; //是否需要核查 op.Deta ...

  10. CRM 报表导出excel时指定sheet名

    如图所示,设置PageName即可: 这样导出excel时,sheet的名就有了: