第一个实际区别是惰性 val 和对象是惰性的,而 val 是渴望的。
对象和惰性 val 之间的主要区别在于,从语言的角度来看,对象被认为是“单例”,从 jvm 的角度来看,它通常被视为静态成员。给定示例中的对象定义不能被覆盖,正如其他人所展示的那样,静态成员不能被覆盖的原因相同:如果不绑定到实例,就无法进行虚函数查找。
object Foo { object Bar extends A; }
大致类似于下面的 java 代码:
class Foo {
private static class Bar extends A{}
public static Bar Bar = new Bar;
}
如果在上面的例子中,如果定义了一个子类 C extends Foo,它就不能覆盖 Bar 的定义。 Java 中的静态实例 Bar 将作为 Foo.Bar 访问。 C.Bar 与 (new C).Bar 的含义不同。我可能有点跑题了,我还没有真正尝试过反编译scala代码,这只是一个例子来说明对象作为静态成员的一般概念。
lazy vals 的效率可能会低一些。上次我检查时,它们是通过在类中维护一个隐藏字段来实现的,该字段跟踪哪些惰性 val 已被初始化。维护此字段需要锁定,这可能会导致性能问题。
lazy val 和 object 之间的一个主要实际区别是对失败的处理:
如果我有:
class Foo() { throw new Exception("blah!"); }
object Stuff { object Bar extends Foo { val x = "hi" } }
Stuff.Bar
//exception "blah!" thrown.
Stuff.Bar.x
//NoClassDefFoundError: Could not initialize Stuff$Bar$
如果我这样做:
object Stuff2 { lazy val Bar = new Foo() { val x = "hi" } }
Stuff2.Bar
// "blah!"
Stuff2.Bar.x
// "blah!"
“NoClassDefFoundError”可能真的令人困惑,因为它是错误而不是异常,它可能会破坏(适当地)捕获/记录“异常”但允许错误传播的错误处理代码。我什至可能会考虑 Scala 语言中的这种错误,因为这个用例确实表明了一种异常情况,而不是真正的 JVM 错误。在访问依赖于外部资源的对象(例如数据库连接或磁盘上的文件)时,我已经看到了 NoClassDefFoundErrors。只有第一次访问会记录根本原因,因此正确调试此类问题通常需要重新启动应用程序服务器。