scala笔记之惰性赋值(lazy)
一、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)的更多相关文章
- es6学习笔记2-解构赋值
解构赋值基本概论就按照一定的模式通过数组或者对象对一组变量进行赋值的过程. 1.通过数组对变量进行赋值: /*通过这种方式赋值要注意左右两边的结构模式要一样,在赋值的时候,根据位置进行赋值对应模式.* ...
- 2018-12-09 疑似bug_中文代码示例之Programming in Scala笔记第九十章
续前文: 中文代码示例之Programming in Scala笔记第七八章 源文档库: program-in-chinese/Programming_in_Scala_study_notes_zh ...
- 2018-11-27 中文代码示例之Programming in Scala笔记第七八章
续前文: 中文代码示例之Programming in Scala学习笔记第二三章 中文代码示例之Programming in Scala笔记第四五六章. 同样仅节选有意思的例程部分作演示之用. 源文档 ...
- RabbitMQ 惰性队列Lazy Queue
RabbitMQ 队列分为几种类型,按照不同维度来分,可以分为排他性队列.普通队列.延迟队列.惰性队列.发布订阅队列等. 今天我们讨论的主角是惰性队列 Lazy Queue.众所周知,队列可以存储消息 ...
- ES6-11学习笔记--解构赋值
解构赋值:按照一定模式,从数组和对象中提取值,对变量进行赋值. 数组解构 对象解构 字符串解构 应用场景 曾经的赋值噩梦,非解构赋值数组: let arr = [1, 2, 3]; let ...
- Scala 笔记
环境 1. Intellij Idea 2. Scala 插件 3. http://scala-lang.org/ 教程: idea官方教程: https://www.jetbrains.com/he ...
- Scala笔记整理
使用类型参数化数组(Array) 创建java.math.BigInteger实例: var big = new java .math.BigInteget("12345678") ...
- es6学习笔记--解构赋值
昨天学习了es6语法中解构赋值,解构赋值在声明中和函数传参提高了灵活性和便捷性,值得掌握该语法. 概念: ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构. 数组的解构 ...
- Programming In Scala笔记-第十七章、Scala中的集合类型
本章主要介绍Scala中的集合类型,主要包括:Array, ListBuffer, Arraybuffer, Set, Map和Tuple. 一.序列 序列类型的对象中包含多个按顺序排列好的元素,可以 ...
随机推荐
- webWorker
一.webWorker之初体验 在"setTimeout那些事儿"中,说到JavaScript是单线程.也就是同一时间只能做同一事情. 也好理解,作为浏览器脚本语言,如果JavaS ...
- C++:继承访问属性(public/protected/private)
• 公有继承(public) 公有继承在C++中是最常用的一种继承方式,我们先来看一个示例: #include<iostream> using namespace std; class F ...
- A02-java学习-classpath配置-标识符-java变量类型
学习 1, classpath的配置和使用 2, java的标识符命名规则和命名规范 3, 字符编码 4, java的变量类型 5, 程序的入口main方法解释
- 2013长春网赛1005 hdu 4763 Theme Section(kmp应用)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4763 题意:给出一个字符串,问能不能在该串的前中后部找到相同的子串,输出最长的字串的长度. 分析:km ...
- 清华集训2015-Day 1
玛里苟斯 一个大小为 \(n\) 的可重集合 \(a\) ,求 \(\mathbb E[x^k]\) ,其中 \(x\) 为 \(a\) 的一个子集的异或和. \(n\le 10^5,1\le k\l ...
- logstash获取日志,时间戳相差8小时
背景: logstash版本:2.4.9 由于logstash在获取时区的时候,默认获取的是UTC默认时间,同时elasticsearch在创建索引的时候,统一使用UTC时间,因此实际上在创建 ...
- pgm6
有个比较有意思的想法是编码理论的反问题是 machine learning,这也是这部分学习的一个收获.这个其实很奇怪,编码理论其实是有 ground truth 的,然后通过编码产生“冗余”,这样才 ...
- Ryuji doesn't want to study 2018 徐州赛区网络预赛
题意: 1.区间求 a[l]×L+a[l+1]×(L−1)+⋯+a[r−1]×2+a[r](L is the length of [ l, r ] that equals to r - l + 1) ...
- Spoj 8372 Triple Sums
题意:给你n个数字,对于任意s,s满足\(s=u_i+u_j+u_k,i<j<k\),要求出所有的s和对应满足条件的i,j,k的方案数 Solution: 构造一个函数:\(A(x)=\s ...
- 敏捷持续集成(Jenkins)
在前面已经完成git和gitlab的相关操作 1.持续集成的概念: 1. 什么是持续集成: 持续集成是一种软件开发实践,即团队开发成员经常集成他们的工作,通过每个成员每天至少集成一次,也就意味着每天可 ...