9

私は誤って次のような状況に遭遇しました (例は問題を特定するために単純化されています)。

abstract class Element(val other: Element)

case object First extends Element(Second)
case object Second extends Element(First)

object Main {
  def main(arguments: Array[String]) {
    val e1 = First
    val e2 = Second
    println("e1: "+e1+"   e1.other: "+e1.other)
    println("e2: "+e2+"   e2.other: "+e2.other)
  }
}

誰もが出力を推測したいですか? :-)

e1: First   e1.other: Second
e2: Second   e2.other: null

出力は一種の理にかなっています。どうやら Second オブジェクトが作成された時点では、First オブジェクトがまだ存在しないため、null割り当てられます。問題は... それはとても間違っている !これを追跡するのに数時間かかりました。コンパイラはこれについて何かを伝えるべきではありませんか? 興味深いことに、これを Scala スクリプト (同じコード、マイナスobject Maindef main行、および終了}s) として実行しようとすると、無限のシーケンスが得られました (実際には無限ではありません - ある時点でリストが停止します。何らかの制限が原因だと思います)。例外トレースの深さ、または何か) で、次のような例外の:

vilius@blackone:~$ scala 1.scala
...
at Main$$anon$1.Main$$anon$$Second(1.scala:4)
at Main$$anon$1$First$.<init>(1.scala:3)
at Main$$anon$1.Main$$anon$$First(1.scala:3)
at Main$$anon$1$Second$.<init>(1.scala:4)
at Main$$anon$1.Main$$anon$$Second(1.scala:4)
at Main$$anon$1$First$.<init>(1.scala:3)
...

実行時に少なくとも同じくらい有益なものを取得したいと思います...

Ok。私は私の暴言を終えました。今、私は何かを尋ねるべきだと思います。:) では、お互いを指しているケースオブジェクトの素敵なデザインをお勧めできますか? ちなみに、私の実際の状況では、次のインスタンスと前のインスタンスを循環的に指すオブジェクトがいくつかあります (最後のインスタンスは最初のインスタンスを指し、その逆も同様です)。

Scala 2.8.1-final の使用

編集: 私は私の主な問題の解決策を見つけました:

abstract class Element {
  val other: Element
}
case object First extends Element {
  val other = Second
}
case object Second extends Element {
  val other = First
}

これはコンパイルされたバージョンで動作するようです (ただし、Scala スクリプトとしては動作しません!)。ここで何が起こっているのか、誰かに光を当てることができますか?

EDIT2:これはスクリプトとして機能します(sを使用するだけで同じことですdef):

abstract class Element { def other: Element }
case object First extends Element { def other = Second }
case object Second extends Element { def other = First }
4

1 に答える 1

10

通常の方法は次のようになります(ネストを変更してREPLに貼り付けることができます)。

object Main{
  abstract class Element(other0: => Element) {
    lazy val other = other0
  }

  case object First extends Element(Second)
  case object Second extends Element(First)

  def main(arguments: Array[String]) {
    val e1 = First
    val e2 = Second
    println("e1: "+e1+"   e1.other: "+e1.other)
    println("e2: "+e2+"   e2.other: "+e2.other)
  }
}

つまり、名前によるパラメーターを取得し、後で参照できるように遅延値に貼り付けます。


編集:オブジェクト自体は参照できるという点で怠惰ですが、使用するまで作成されないため、見つかった修正は機能します。したがって、一方のオブジェクトは、もう一方のオブジェクトがすでに初期化されている必要なしに、もう一方のオブジェクトを自由に指すことができます。

于 2011-02-12T13:35:09.060 に答える