在Scala类中,声明字段为val
、lazy val
和object
有什么区别?例如下面的代码片段:
class A
class B {
val a1 = new A { def foo = 1 }
object a2 extends A { def foo = 1 }
lazy val a3 = new A { def foo = 1 }
}
在前者中,任何包含的代码都会在B类创建时立即执行。然而,在后者中,除非你实际使用该对象,否则它不会被实例化。
你可以在这里看到区别:
class A { println("Creating a new A") }
class B {
val a1 = new A { println("a1"); def foo = 1 }
object a2 extends A { println("a2"); def foo = 1 }
}
scala> val b = new B
Creating a new A
a1
b: B = B@1176e8a
scala> b.a2.foo
Creating a new A
a2
res0: Int = 1
创建的 .class 文件的命名等方面也存在隐藏的差异;当然,这两种类型也是不同的。
object
的工作方式类似于 lazy val
。这两者之间有任何实际区别吗? - PeWuobject
的字节码更加紧凑一些。我不确定这是否意味着 lazy val
的编写效率低下,或者在某些线程条件下 object
可能不安全,或者两者都有可能。 - Rex Kerrlazy val
代替对象! - Elazar Leibovich我不确定aioobe是否认识到他的答案的重要性,但不同类型实际上代表了vals
和objects
之间的关键差异。特别地,val
和lazy val
具有结构类型(例如A{def foo: Int}
),而object
具有单例类型。因此,在val
上调用foo
方法会涉及反射,而在object
上调用foo
方法则不会:
class A
class B {
val a1 = new A { def foo = printStack }
object a2 extends A { def foo = printStack }
lazy val a3 = new A { def foo = printStack }
def printStack() =
new Exception().getStackTrace take 3 foreach println
}
scala> val b = new B
b: B = B@5c750
scala> b.a1.foo // the val
line124$object$$iw$$iw$B.printStack(<console>:12)
line124$object$$iw$$iw$B$$anon$1.foo(<console>:7)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
scala> b.a2.foo // the object
line124$object$$iw$$iw$B.printStack(<console>:12)
line124$object$$iw$$iw$B$a2$.foo(<console>:8)
line128$object$$iw$$iw$.<init>(<console>:9)
scala> b.a3.foo // the lazy val
line124$object$$iw$$iw$B.printStack(<console>:12)
line124$object$$iw$$iw$B$$anon$2.foo(<console>:9)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
foo
方法,即使是抽象的定义,就不需要使用反射了。 - Aaron Novstrup一个主要的区别是 val 可以被覆盖,而对象不能。
class C extends B {
override val a1 = new A { def foo = 2 }
override object a2 extends A { def foo = 2 }
}
导致:
<console>:9: error: overriding object a2 in class B of type object C.this.a2;
object a2 cannot be used here - classes and objects cannot be overridden
override object a2 extends A { def foo = 2 }
我想不同之处在于a1
将属于A
的一个子类型,而a2
将属于A
的另一个子类型,即a2.type
。
scala> class A
defined class A
scala> val a1 = new A {def foo = 1}
a1: A{def foo: Int} = $anon$1@a9db0e2
scala> object a2 extends A {def foo = 1}
defined module a2
scala> a1
res0: A{def foo: Int} = $anon$1@a9db0e2
scala> a2
res1: a2.type = a2$@5b25d568
scala>
a1
的类型不是 A
;该类型是您创建的匿名子类的类型。您可以通过显式指定类型来将其设置为A
:val a1: A = new A {def foo=1}
。 - Jespera1
的类型并不是匿名子类的类型;它是结构类型A{def foo: Int}
,与匿名子类不同之处在于调用foo
将使用反射。您可以通过让foo
打印堆栈跟踪来验证这一点。 - Aaron Novstrup第一个实用差别在于,lazy vals 和 objects 是惰性加载的,而 vals 是急切加载的。
对象和 lazy vals 的主要区别在于,从语言角度来看,对象被认为是“单例”的,而从 jvm 角度来看,它通常被视为静态成员。给定示例中的对象定义不能被重写,就像其他人已经证明的那样,因为没有被绑定到实例,所以没有可想象的方法进行虚函数查找。
object Foo { object Bar extends A; }
它大致像以下 Java 代码:
class Foo {
private static class Bar extends A{}
public static Bar Bar = new Bar;
}
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!"
这不是结构类型:val a = new A { def foo = 1 }
它创建了一个唯一的匿名子类;a.foo在该类中调用foo。
x在此处是结构类型:def bar(x: { def bass: Int })
x.bass将检查x(未知类型)以查找名称为“bass”的方法。它适用于鱼或乐器。 ;)
lazy val和object之间的一个区别是:
var someA = (new B).a3
someA = (new B).a3 // ok
var anotherA = (new B).a2
anotherA = = (new B).a2 // compile error
lazy val a3 = new A { def foo = 1 }
也添加到问题中。 - PeWu