为什么 Scala 中的辅助构造函数只能由对另一个构造函数的一次调用组成? [英] Why can auxiliary constructors in Scala only consist of a single call to another constructor?

查看:40
本文介绍了为什么 Scala 中的辅助构造函数只能由对另一个构造函数的一次调用组成?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I think I see the merit in defining auxiliary constructors in such a way that the primary constructor is the solitary point of entry to the class. But why can't I do something like this?

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

是否可以保证辅助构造函数没有副作用和/或不能提前返回?

Is it maybe a way of guaranteeing that the auxiliary constructor doesn't have side effects and/or can't return early?

推荐答案

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

Auxiliary constructors can contain more than a single invocation of another constructor, but their first statement must be said invocation.

Scala 编程中所述,ch.6.7:

As explained in Programming in Scala, ch. 6.7:

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

In Scala, every auxiliary constructor must invoke another constructor of the same class as its first action. In other words, the first statement in every auxiliary constructor in every Scala class will have the form this(. . . ). The invoked constructor is either the primary constructor (as in the Rational example), or another auxiliary constructor that comes textually before the calling constructor. The net effect of this rule is that every constructor invocation in Scala will end up eventually calling the primary constructor of the class. The primary constructor is thus the single point of entry of a class.

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

If you’re familiar with Java, you may wonder why Scala’s rules for constructors are a bit more restrictive than Java’s. In Java, a constructor must either invoke another constructor of the same class, or directly invoke a constructor of the superclass, as its first action. In a Scala class, only the primary constructor can invoke a superclass constructor. The increased restriction in Scala is really a design trade-off that needed to be paid in exchange for the greater conciseness and simplicity of Scala’s constructors compared to Java’s.

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

Just as in Java, one can get round this limitation by extracting the code to be executed before the primary constructor call into a separate method. In Scala it is a bit more tricky than in Java, as apparently you need to move this helper method into the companion object in order to be allowed to call it from the constructor.

此外,您的具体情况很尴尬,因为您有两个构造函数参数,并且 - 尽管可以从函数返回元组 - 然后不接受此返回的元组作为主构造函数的参数列表.对于普通函数,可以使用tupled,但是,这似乎不适用于构造函数.一种解决方法是添加另一个辅助构造函数:

Moreover, your specific case is awkward as you have two constructor parameters and - although one can return tuples from a function - this returned tuple is then not accepted as the argument list to the primary constructor. For ordinary functions, you can use tupled, but alas, this doesn't seem to work for constructors. A workaround would be to add yet another auxiliary constructor:

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

}

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

This at least works, even if it is slightly complicated.

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 在他的评论中指出的那样,这可以使用伴随对象中的工厂方法来简化:

Update

As @sschaef's pointed out in his comment, this can be simplified using a factory method in the companion object:

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 来创建对象:

Thus we need no new to create an object anymore:

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

init
Wobble wibble
w: Wobble = Wobble@47c130

这篇关于为什么 Scala 中的辅助构造函数只能由对另一个构造函数的一次调用组成?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆