在“Effective Scala”的懒惰部分,它说:
当 val 以lazy 为前缀时,scala 中的字段由需要计算。因为字段和方法在 Scala 中是等价的(以免字段是私有的[this])
“字段”和“方法”等价是什么意思?这不是一个相当强烈的声明吗?
好吧,它只是意味着您可以通过 a 定义一个抽象val
,def
并def
通过a 定义一个抽象val
。
例如
trait Server {
def port:Int
}
有一个抽象函数,即port
。您可以肯定地用 a 实现(或定义或覆盖)它def
,就像这样
object DefaultServer extends Server {
override def port: Int = 80
}
但是在这种情况下,每次访问port
都会导致一个函数应用程序(或一个方法调用),这完全是不必要的。出于这个简单的原因,Scala 为我们提供了用值实现抽象的可能性def
:
object DefaultServer extends Server {
override val port: Int = 80
}
这同样适用于抽象值。您可以使用相同的语法定义抽象值:
trait Server {
val port: Int
}
您可以使用以下命令覆盖它们def
:
object DefaultServer extends Server {
override def port: Int = 80
}
稳定
你可能想知道如果你val
用 a覆盖一个抽象会发生什么def
,每次调用它都会给你一个不同的值。您会因为项目是 a 而获得第一个计算值,val
还是因为实际实现是 a ,所以每次调用它时都会获得不同的值def
。
例如:
object DefaultServer extends Server {
override def port: Int = scala.util.Random.nextInt()
}
幸运的是 Scala 编译器检测到这一点并抛出以下错误:
error: overriding value a in trait Server of type Int;
method a needs to be a stable, immutable value
override def port:Int = scala.util.Random.nextInt()
懒惰
当涉及到惰性时,这种统一的行为(以相同的方式处理字段和方法)非常有用。
首先请注意,lazy
抽象值不存在,即您不能将抽象定义val
为lazy
.
另一方面,def
通过 a实现抽象是完全合法且有用的。lazy val
该lazy
值将仅在第一次调用时计算并记忆(缓存并用于将来的调用)。
主要原因可能是,lazy val
并且def
在使用之前计算(而不是在首次定义时)。
不同之处在于lazy val
试图避免重新评估不变的值,这增加了将计算值保存在内存中的成本。