Scala中的Case类的不可变配对实例?

时间:2018-02-25 11:46:57

标签: scala

我试图塑造一种可以逆转的关系。例如,北方的反面可能是南方。左派的反面可能是正确的。我想用一个案例类来表示我的关系。我找到了一个类似的解决方案,在这里使用了案例对象,但它并不是我想要的,here

这是我的非功能性代码:

Promise.resolve(window.gapi.auth2.getAuthInstance().signIn()).then(function () {
        // Listen for sign-in state changes.
        gapi.auth2.getAuthInstance().isSignedIn.listen(googlePeopleSignedInCallback);
        // Handle the initial sign-in state.
        googlePeopleSignedInCallback(gapi.auth2.getAuthInstance().isSignedIn.get());
    }, function (error) {
        console.log(error);
    });

可以更改此代码,以便:

  • 它不会崩溃
  • 我可以成对创造这些东西。

2 个答案:

答案 0 :(得分:4)

如果你真的想构建具有循环依赖关系的不可变对象的图形,你必须将opposite声明为def,并且(最好)将另外一个懒惰的val添加到混合中:

abstract class Relationship(val name: String) {
  def opposite: Relationship
}

object Relationship {

  /** Factory method */
  def apply(nameA: String, nameB: String): Relationship = {
    lazy val x: Relationship = new Relationship(nameA) {
      lazy val opposite = new Relationship(nameB) {
        def opposite = x
      }
    }

    x
  }

  /** Extractor */
  def unapply(r: Relationship): Option[(String, Relationship)] =
    Some((r.name, r.opposite))

}

val ns = Relationship("North", "South")

println(ns.name)
println(ns.opposite.name)
println(ns.opposite.opposite.name)
println(ns.opposite.opposite.opposite.name)

如果你在这个循环依赖圈子上运行几百万轮,你可以很快说服自己没有任何不好的事情发生:

// just to demonstrate that it doesn't blow up in any way if you
// call it hundred million times:
// Should be "North"
println((1 to 100000000).foldLeft(ns)((r, _) => r.opposite).name)

确实打印了" North"。它不适用于案例类,但您可以随时添加自己的提取器,因此可以使用:

val Relationship(x, op) = ns
val Relationship(y, original) = op
println(s"Extracted x = $x y = $y")

它打印" North"和"南" xy

然而,更明显的做法是仅保存关系的两个组件,并添加opposite作为构造相反对的方法。

case class Rel(a: String, b: String) {
  def opposite: Rel = Rel(b, a)
}

实际上,这已在标准库中实现:

scala> val rel = ("North", "South")
rel: (String, String) = (North,South)

scala> rel.swap
res0: (String, String) = (South,North)

答案 1 :(得分:2)

你有循环依赖,这不会起作用。一种选择是:

case class Relationship(name: String)

并设置一个指定相反的setter。然后工厂会做:

def relationshipFactory(nameA:String, nameB:String): Relationship = {
  val x:Relationship = Relationship(nameA)
  val opposite = Relationship(nameB)

  x.setOpposite(opposite)
  opposite.setOpposite(x)
  x
}

另一种选择:

case class Relationship(name: String) {
  lazy val opposite = Utils.computeOpposite(this)
}

并且在Utils对象上具有相反的逻辑

还有另一种选择:可能你不想要几个South个实例,因此你应该使用案例对象或枚举(更多内容见http://pedrorijo.com/blog/scala-enums/

使用枚举,您可以使用模式匹配来执行该逻辑,而无需开销