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での制限の増加は、実際には設計上のトレードオフであり、Javaと比較してScalaのコンストラクターがより簡潔で単純であることと引き換えに支払う必要がありました。

Javaの場合と同様に、プライマリコンストラクターが別のメソッドを呼び出す前に実行されるコードを抽出することで、この制限を回避できます。Scalaでは、Javaよりも少し注意が必要です。コンストラクターから呼び出すことができるようにするには、このヘルパーメソッドをコンパニオンオブジェクトに移動する必要があるようです。

さらに、2つのコンストラクターパラメーターがあり、関数からタプルを返すことはできますが、この返されたタプルはプライマリコンストラクターの引数リストとして受け入れられないため、特定のケースは厄介です。通常の関数の場合はを使用できます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 に答える