我知道有两种方法可以让一个匿名类在Scala中实例化一个特征:
scala> trait SomeTrait {
| def aUsefulMethod = ()
| }
defined trait SomeTrait
scala> val instance1 = new SomeTrait{} // Method 1
instance1: SomeTrait = $anon$1@7307556f
scala> instance1.aUsefulMethod // Returns a Unit.
scala> object instance2 extends SomeTrait // Method 2
defined module instance2
scala> instance2.aUsefulMethod // Returns a Unit.
我想不出他们不等同的原因。我错了吗?
我问的部分是因为我以前只知道方法2,但现在我看到方法1更常见。所以我想知道我是否一直在做错事。
答案 0 :(得分:4)
第一种方法new Trait {}
创建了一个新的class
- 实例。
第二种方法创建一个object
,它是一个单身人士。
人们可以在REPL中看到这一点:
scala> trait Example {}
defined trait Example
每次调用new都会返回一个新实例。可以看出每个对象都有一个新地址。
scala> new Example{}
res0: Example = $anon$1@768debd
scala> new Example{}
res1: Example = $anon$1@546a03af
这里创建一个单例对象。
scala> object X extends Example
defined object X
scala> X
res2: X.type = X$@1810399e
scala> X
res3: X.type = X$@1810399e
即使表面上的两种方法看起来都相似,也会导致不同的结果。
scala> new Example{} == new Example{}
<console>:12: warning: comparing values of types Example and Example using `==' will always yield false
new Example{} == new Example{}
^
res4: Boolean = false
scala> X == X
res5: Boolean = true
在底层结构上,两个aproach都会导致在*class
上运行时生成不同的JVM
文件
$ cat example.scala
object Example1 {
trait A
new A {}
}
$ scalac example.scala
$ ls *class
Example1$$anon$1.class Example1$A.class
Example1$.class Example1.class
$ cat example2.scala
object Example2 {
trait A
object X extends A
}
$ scalac example2.scala
$ ls *class
Example2$.class Example2$X$.class
Example2$A.class Example2.class
答案 1 :(得分:3)
val instance1 = new SomeTrait{}
与
class X extends SomeTrait
val instance1: SomeTrait = new X
除了编译器创建class X
并为其命名$anon$1
之外。如果您执行val instance2 = new SomeTrait{}
,编译器会注意到它可以重用相同的匿名类。 object instance2
基本上也是
class instance2$ extends SomeTrait {
override def toString = "instance2"
}
lazy val instance2 = new instance2$
除了您无法创建instance2$
的新实例。所以一个区别就是懒惰的实例化:instance2
实际上只是在访问时才创建(例如当你调用instance2.aUsefulMethod
时),如果SomeTrait
构造函数抛出异常或者有其他方面则会产生差异效果。另一个原因是您可以在顶层object
,class
或trait
之外使用object
。