“Array.fill(2)(new A)”和“val a = new A; Array.fill(2)(a)”之间有什么区别?

时间:2012-12-05 22:35:20

标签: class scala new-operator cloning scala-2.9

最小工作示例(Scala 2.9.2):

object Main extends App {
  class A {
    var a=0
  }
  val b = Array.fill(2)(new A)
  b(1).a = 9
  println(b(0).a) //prints 0
  println(b(1).a) //prints 9

  val a = new A
  val c = Array.fill(2)(a)
  c(1).a = 9
  println(c(0).a) //prints 9
  println(c(1).a) //prints 9
}

一个相关的问题是“导入的Java类是否相同?” 如果我需要在函数内填充一个带有作为参数传递的实例的副本的数组,我该如何解决? [关于相同的副本,我可以查看easy cloning library

只需根据答案将变通方法添加到函数调用中:

  class A {
    var a=0
  }

  def f(a: => A) {             // "=>" added
    val b = Array.fill(2)(a)
    b(1).a=9
    println(b(0).a) //prints 0
    println(b(1).a) //prints 9
  }

  f(new A)

另一种方法是声明一个函数,而不是值def a = new A

object Main extends App {
  class A {
    var a=0
  }
  val b = Array.fill(2)(new A)
  b(1).a = 9
  println(b(0).a) //prints 0
  println(b(1).a) //prints 9

  def a = new A
  val c = Array.fill(2)(a)
  c(1).a = 9
  println(c(0).a) //prints 0
  println(c(1).a) //prints 9
}

3 个答案:

答案 0 :(得分:7)

fill方法定义为第二个参数为“call-by-name”。这意味着对于Array中的每个单元,传入的块都将被重新评估。请参阅定义,elem的类型为=> T,而不仅仅是T

def fill[T: ClassManifest](n: Int)(elem: => T): Array[T]

因此,在您的第一个版本中,每个单元格都会重新评估块new A,这意味着每个单元格都会获得一个新的A对象。在第二个版本中,new A仅被调用一次,并且该对象被放置在每个单元格中。

如果你在REPL上运行,你实际上可以看到这个:

scala> val b = Array.fill(2)(new A)
b: Array[A] = Array(A@2049bed2, A@498edd8d)    // two different objects

scala> val c = Array.fill(2)(a)
c: Array[A] = Array(A@31e0c0b6, A@31e0c0b6)    // the same object repeated

答案 1 :(得分:2)

看一下填充的签名

def fill[T: ClassManifest](n: Int)(elem: => T)

因此它获得了一个按名称调用的参数,这意味着每次处理数组中的单元格时都会执行new A

答案 2 :(得分:1)

一个用A的单个实例填充数组,另一个用A的新实例填充数组。