使用new运算符定义对象和通过扩展类定义独立对象有什么区别?
更具体地说,对于类型class GenericType { ... }
,val a = new GenericType
和object a extends GenericType
之间有什么区别?
使用new运算符定义对象和通过扩展类定义独立对象有什么区别?
更具体地说,对于类型class GenericType { ... }
,val a = new GenericType
和object a extends GenericType
之间有什么区别?
object
声明与字节码中的new
使用相同的机制进行初始化。但是,它们之间存在许多区别:
object
作为单例——每个对象属于只存在一个实例的类;object
被惰性地初始化——只有在第一次引用时才会创建/初始化;object
和class
(或trait
)是伴生对象;object
上定义的方法会在伴生class
上生成静态转发器;object
的成员可以访问伴生class
的私有成员;scala
标签的维基百科页面。对象定义(无论它是否扩展其他对象)意味着单例对象创建。
scala> class GenericType
defined class GenericType
scala> val a = new GenericType
a: GenericType = GenericType@2d581156
scala> val a = new GenericType
a: GenericType = GenericType@71e7c512
scala> object genericObject extends GenericType
defined module genericObject
scala> val a = genericObject
a: genericObject.type = genericObject$@5549fe36
scala> val a = genericObject
a: genericObject.type = genericObject$@5549fe36
虽然 object
声明语义与 new
表达式不同,但是一个本地的 object
声明在实际上和同名的 lazy val
是一样的。例如:
class Foo( name: String ) {
println(name+".new")
def doSomething( arg: Int ) {
println(name+".doSomething("+arg+")")
}
}
def bar( x: => Foo ) {
x.doSomething(1)
x.doSomething(2)
}
def test1() {
lazy val a = new Foo("a")
bar( a )
}
def test2() {
object b extends Foo("b")
bar( b )
}
test1
将a
定义为一个懒加载的变量,其初始化值为Foo
类的一个新实例。而test2
将b
定义为一个继承Foo
的object
。
实质上,它们都会懒加载创建一个新的Foo
实例,并赋予变量名(a
/b
)。
您可以在REPL中尝试并验证它们的行为相同:
scala> test1()
a.new
a.doSomething(1)
a.doSomething(2)
scala> test2()
b.new
b.doSomething(1)
b.doSomething(2)
object
和lazy val
之间存在语义差异(特别是object
被语言特殊处理的情况,如Daniel C. Sobral所述),但lazy val
总是可以用相应的object
进行替换(虽然这不是一个很好的实践方法),对于属于类/特质的lazy val
/object
也是如此。我能想到的主要实际区别是对象具有更具体的静态类型:b
的类型为b.type
(它扩展了Foo
),而a
的类型恰好为Foo
。