一、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=" 06 01}2A! 03 06 01#!)q 03 01C 011!A1 04 01EC 02 23 05A 04C 04&