使用 new 运算符定义对象与通过扩展类定义独立对象有什么区别?
更具体地说,给定类型class GenericType { ... }
,val a = new GenericType
和object a extends GenericType
有什么区别?
实际上,object
声明的初始化机制与字节码中的new
相同。但是,存在很多差异:
-
object
为单例 -- 每个都属于一个仅存在一个实例的类; -
object
是延迟初始化的——它们只会在第一次引用时创建/初始化;
同 - 名的
object
和class
(或trait
(是同伴; - 在 上定义的方法
object
在配套class
上生成静态转发器; object
成员可以访问同伴class
的私人成员;- 搜索隐式时,会研究相关*类或特征的伴随对象。
这些只是我能想到的一些差异。可能还有其他人。
* 什么是"相关"类或特征是一个更长的故事 - 如果你有兴趣,可以在 Stack Overflow 上查找解释它的问题。如果您在查找 wiki 时找不到 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
的新实例初始化的惰性 val,而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
。