14

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

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)
  }
}

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

4

1 回答 1

22

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

正如在 Scala 编程中所解释的那样,ch。6.7:

在 Scala 中,每个辅助构造函数都必须调用与其第一个操作相同的类的另一个构造函数。换句话说,每个 Scala 类中的每个辅助构造函数中的第一条语句都将具有this(. . . ). 调用的构造函数要么是主构造函数(如Rational 示例中所示),要么是另一个辅助构造函数,它以文本形式出现在调用构造函数之前。这条规则的最终结果是 Scala 中的每个构造函数调用最终都会调用该类的主构造函数。因此,主构造函数是类的单一入口点。

如果您熟悉 Java,您可能想知道为什么 Scala 的构造函数规则比 Java 的规则更严格一些。在 Java 中,构造函数必须要么调用同一个类的另一个构造函数,要么直接调用超类的构造函数,作为它的第一个动作。在 Scala 类中,只有主构造函数可以调用超类构造函数。Scala 中增加的限制实际上是一种设计权衡,需要付出代价以换取 Scala 的构造函数比 Java 更简洁和简单。

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

此外,您的具体情况很尴尬,因为您有两个构造函数参数,并且 - 尽管可以从函数返回元组 - 这个返回的元组然后不被接受作为主构造函数的参数列表。对于普通函数,您可以使用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
于 2013-01-08T08:25:10.387 回答