在 Scala 中(某种)实例化特征的两种方法
Two ways of (kind of) instantiating a trait in Scala
我知道在 Scala 中创建匿名 class 来实例化特征的两种方法:
scala> trait SomeTrait {
| def aUsefulMethod = ()
| }
defined trait SomeTrait
scala> val instance1 = new SomeTrait{} // Method 1
instance1: SomeTrait = $anon@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 更常见。所以我想知道我是否一直做错了什么。
第一种方法 new Trait {}
创建一个新的 class
实例。
第二种方法创建一个object
,它是一个单例。
可以在 REPL 中看到:
定义特征
scala> trait Example {}
defined trait Example
新匿名class
每次调用 new 都会 return 一个新实例。可以看出每个对象都有一个新地址。
scala> new Example{}
res0: Example = $anon@768debd
scala> new Example{}
res1: Example = $anon@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
更深入
在底层结构上,当 JVM
上的 运行 时,两种方法都会导致生成不同的 *class
文件
匿名class
$ cat example.scala
object Example1 {
trait A
new A {}
}
$ scalac example.scala
$ ls *class
Example1$$anon.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
val instance1 = new SomeTrait{}
等同于
class X extends SomeTrait
val instance1: SomeTrait = new X
除了编译器创建 class X
并给它起一个像 $anon
这样的名字。如果您随后执行 val instance2 = new SomeTrait{}
,编译器会注意到它可以重用相同的匿名 class。而object instance2
也基本上是
class instance2$ extends SomeTrait {
override def toString = "instance2"
}
lazy val instance2 = new instance2$
除非您无法创建 instance2$
的新实例。因此,一个区别是惰性实例化:instance2
仅在访问时才实际创建(例如,当您调用 instance2.aUsefulMethod
时),如果 SomeTrait
构造函数抛出异常或有其他方面,这会有所不同效果。另一个是您可以在顶层使用 object
(在 class
、trait
或 object
之外)。
我知道在 Scala 中创建匿名 class 来实例化特征的两种方法:
scala> trait SomeTrait {
| def aUsefulMethod = ()
| }
defined trait SomeTrait
scala> val instance1 = new SomeTrait{} // Method 1
instance1: SomeTrait = $anon@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 更常见。所以我想知道我是否一直做错了什么。
第一种方法 new Trait {}
创建一个新的 class
实例。
第二种方法创建一个object
,它是一个单例。
可以在 REPL 中看到:
定义特征
scala> trait Example {}
defined trait Example
新匿名class
每次调用 new 都会 return 一个新实例。可以看出每个对象都有一个新地址。
scala> new Example{}
res0: Example = $anon@768debd
scala> new Example{}
res1: Example = $anon@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
更深入
在底层结构上,当 JVM
*class
文件
匿名class
$ cat example.scala
object Example1 {
trait A
new A {}
}
$ scalac example.scala
$ ls *class
Example1$$anon.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
val instance1 = new SomeTrait{}
等同于
class X extends SomeTrait
val instance1: SomeTrait = new X
除了编译器创建 class X
并给它起一个像 $anon
这样的名字。如果您随后执行 val instance2 = new SomeTrait{}
,编译器会注意到它可以重用相同的匿名 class。而object instance2
也基本上是
class instance2$ extends SomeTrait {
override def toString = "instance2"
}
lazy val instance2 = new instance2$
除非您无法创建 instance2$
的新实例。因此,一个区别是惰性实例化:instance2
仅在访问时才实际创建(例如,当您调用 instance2.aUsefulMethod
时),如果 SomeTrait
构造函数抛出异常或有其他方面,这会有所不同效果。另一个是您可以在顶层使用 object
(在 class
、trait
或 object
之外)。