为什么Scala中的辅助构造函数只能由对另一个构造函数的单个调用组成?

时间:2013-01-08 03:58:47

标签: scala

我认为我看到了以这样一种方式定义辅助构造函数的优点,即主构造函数是类的单独入口点。但为什么我不能这样做呢?

class Wibble(foo: Int, bar: String) {
  def this(baz: List[Any]) = {
    val bazLength = baz.length
    val someText = "baz length is " ++ bazLength.toString
    this(bazLength, someText)
  }
}

这可能是一种保证辅助构造函数没有副作用和/或不能提前返回的方法吗?

1 个答案:

答案 0 :(得分:18)

辅助构造函数可以包含多个另一个构造函数的调用,但它们的第一个语句必须是所谓的调用。

Scala中的编程中所述,ch。 6.7:

  

在Scala中,每个辅助构造函数都必须调用另一个构造函数   与第一个动作相同的类。换句话说,每一个中的第一个陈述   每个Scala类中的辅助构造函数都将具有this(. . . )形式。   被调用的构造函数是主要构造函数(如Rational中所示)   示例),或者在文本之前出现的另一个辅助构造函数   调用构造函数。此规则的净效果是每个构造函数调用   在Scala中最终会调用它的主要构造函数   类。因此,主要构造函数是类的单一入口点。

     

如果你熟悉Java,你可能想知道为什么Scala的规则   构造函数比Java更具限制性。在Java中,一个构造函数   必须要么调用同一个类的另一个构造函数,要么直接调用   超类的构造函数,作为它的第一个动作。在Scala类中,只有   主构造函数可以调用超类构造函数。增加了   Scala中的限制实际上是需要支付的设计权衡   交换Scala构造函数的简洁性和简洁性   与Java的相比。

就像在Java中一样,通过在主构造函数调用单独的方法之前提取要执行的代码,可以绕过此限制。在Scala中,它比Java更棘手,因为显然你需要将这个辅助方法移动到伴随对象中,以便允许从构造函数中调用它。

此外,你的具体情况很尴尬,因为你有两个构造函数参数 - 尽管一个可以从一个函数返回元组 - 然后这个返回的元组不被接受作为主构造函数的参数列表。 For ordinary functions, you can use tupled,但是,这似乎对构造函数不起作用。解决方法是添加另一个辅助构造函数:

object Wibble {

  private def init(baz: List[Any]): (Int, String) = {
    val bazLength = baz.length
    val someText = "baz length is " ++ bazLength.toString
    println("init")
    (bazLength, someText)
  }
}

class Wibble(foo: Int, bar: String) {

  println("Wibble wobble")

  def this(t: (Int, String)) = {
    this(t._1, t._2)
    println("You can execute more code here")
  }

  def this(baz: List[Any]) = {
    this(Wibble.init(baz))
    println("You can also execute some code here")
  }

}

这至少有效,即使它有点复杂。

scala> val w = new Wibble(List(1, 2, 3))

init
Wibble wobble
You can execute more code here
You can also execute some code here
w: Wibble = Wibble@b6e385

更新

正如@ sschaef在他的评论中指出的那样,可以使用随播对象中的工厂方法简化:

object Wobble {

  def apply(baz: List[Any]): Wobble = {
    val bazLength = baz.length
    val someText = "baz length is " ++ bazLength.toString
    println("init")
    new Wobble(bazLength, someText)
  }
}

class Wobble(foo: Int, bar: String) {
  println("Wobble wibble")
}

因此我们不再需要new来创建一个对象:

scala>  val w = Wobble(List(1, 2, 3))

init
Wobble wibble
w: Wobble = Wobble@47c130