案例类和特征的线性化

时间:2012-03-14 15:01:16

标签: scala traits case-class

假设我想编写一个案例类Stepper,如下所示:

case class Stepper(step: Int) {def apply(x: Int) = x + step}

它带有一个很好的toString实现:

scala> Stepper(42).toString
res0: String = Stepper(42)

但它不是真正的功能:

scala> Some(2) map Stepper(2)
<console>:10: error: type mismatch;
 found   : Stepper
 required: Int => ?
              Some(2) map Stepper(2)

解决方法是实施Function特征......

case class Stepper(step: Int) extends (Int => Int) {def apply(x: Int) = x + step}

但是,我不能再免费获得一个很好的toString实现了:

scala> Stepper(42).toString
res2: java.lang.String = <function1>

然后,问题是:我可以充分利用这两个世界吗?有没有一个解决方案,我有一个很好的toString实现免费 AND trait Function的实现。换句话说,有没有办法以最终应用case class语法糖的方式应用线性化?

3 个答案:

答案 0 :(得分:8)

问题与线性化无关。在case-classes中,toString是一种由编译器自动生成的方法,当且仅当Any.toString未在end-type中被覆盖时。

然而,答案部分与线性化有关 - 我们需要使用编译器生成的方法覆盖Function1.toString,如果不是Function1引入的版本:

trait ProperName extends Product {
  override lazy val toString = scala.runtime.ScalaRunTime._toString(this)
}

// now just mix in ProperName and... magic!
case class Stepper(step: Int) extends (Int => Int) with ProperName {
  def apply(x:Int) = x+step
}

然后

println(Some(2) map Stepper(2))
println(Stepper(2))

将产生

Some(4)
Stepper(2)

<强>更新

以下是ProperName特征的一个版本,它不依赖于未记录的API方法:

trait ProperName extends Product {
  override lazy val toString  = {
    val caseFields = {
       val arity = productArity
       def fields(from: Int): List[Any] =
         if (from == arity) List()
         else productElement(from) :: fields(from + 1)
       fields(0) 
    }
    caseFields.mkString(productPrefix + "(", ",", ")")
  }
}

替代toString实施源自原始_toString方法scala.runtime.ScalaRunTime._toString的源代码。

请注意,此替代实现仍基于假设案例类始终扩展Product特征。虽然后者在Scala 2.9.0中是正确的,并且是Scala社区的一些成员已知和依赖的事实,但它并未正式记录为Scala Language Spec的一部分。

答案 1 :(得分:2)

编辑:覆盖toString怎么样?

case class Stepper(step: Int) extends (Int => Int) {
  def apply(x: Int) = x + step
  override def toString = "Stepper(" + step + ")"
}

答案 2 :(得分:1)

您可以使用隐式转换,仅在必要时将Stepper视为函数:

case class Stepper(step: Int) { def apply(x: Int) = x + step }

implicit def s2f(s: Stepper) = new Function[Int, Int] {
  def apply(x: Int) = s.apply(x)
}

现在,当您致电toString时,您会收到案例类Stepper(42).toString,但Some(2) map Stepper(2)也会按照需要运作。

(请注意,为了保持机制清晰,我比上面的要求更加冗长。你也可以写implicit def s2f(s: Stepper) = s.apply _或其他更简洁的配方。