在Scala中实例化特征的两种方式(种类)

时间:2015-12-26 13:12:36

标签: scala anonymous-class traits

我知道有两种方法可以让一个匿名类在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更常见。所以我想知道我是否一直在做错事。

2 个答案:

答案 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构造函数抛出异常或者有其他方面则会产生差异效果。另一个原因是您可以在顶层objectclasstrait之外使用object