10

假设我想写一个case类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免费提供很好的实现trait 的实现Function。换句话说,有没有办法以最终应用case class语法糖的方式应用线性化?

4

3 回答 3

8

问题与线性化无关。在 case-classestoString是编译器自动生成的方法,当且仅当Any.toString没有在最终类型中被覆盖。

然而,答案部分与线性化有关——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 版本:

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的一部分。

于 2012-03-14T17:18:40.327 回答
2

编辑:覆盖 toString 怎么样?

case class Stepper(step: Int) extends (Int => Int) {
  def apply(x: Int) = x + step
  override def toString = "Stepper(" + step + ")"
}
于 2012-03-14T15:07:33.777 回答
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 _或任何其他更简洁的公式)。

于 2012-03-14T16:27:12.993 回答