3

我有一个现有的案例类,我想向其中添加一个额外的构造函数参数,具有默认值,并且不干扰执行模式匹配的现有代码,但我无法找出正确的方法。

IE。我有:

case class Foo(a: Int, b: Int)

def bar(n: Foo) = n match {
  case Foo(1, 2) => true
  case _ => false
}

现在假设我需要向 Foo 添加一个附加参数 c。IE。

case class Foo(a: Int, b: Int, c: Boolean = true)

在所有现有的用例中,该参数c将为 true,因此它具有该默认值。然而在一些新的用例中,需要为此传递 false 。

因此,添加另一个参数似乎是明智的,默认值为true. 然而,一旦我这样做,模式匹配bar就会变成语法错误。这似乎是错误的,因为我添加了默认值= true以确保不需要修改现有的构造函数调用。

我怎样才能做到这一点并保持旧模式匹配不变?

更新:请注意,我也不想重写 Foo 的所有现有实例。@som-snytt 指出我可以添加另一个参数 as Foo(a: Int, b: Int)(c: Boolean = true),这将是完美的,只是它会导致现有调用Foo(1,2)失败(例如,它们必须重写为Foo(1,2)())。我正在寻找一种仅为某些用例添加新参数的方法,并通过使用适用于其他任何地方的默认值来避免重写。

4

2 回答 2

2

case Foo语法不是调用构造函数,而是调用unapplyon的方法object Foo。case 类自动生成各种样板文件,包括伴随对象和 unapply。

unapply只有一个参数,被匹配的对象。这可以防止您重载它,因为您不能在 Java/Scala 中重载返回值。

所以简而言之,你不能做你想做的事。

但是,您可以使用不同的名称制作提取器。这是刚刚添加下划线的人:http: //x3ro.de/multiple-unapply-methods-for-pattern-matching-in-scala/

不过,如果可能的话,为提取器变体使用更有意义的名称可能会更好。

以下是有关这一切如何运作的更多信息:http: //danielwestheide.com/blog/2012/11/21/the-neophytes-guide-to-scala-part-1-extractors.html

您可以写出案例类“手动”执行的所有操作,并以不同的方式执行,例如不同的 unapply,但假设您关心 equals、hashCode、toString 等所有内容,这将非常烦人。如果你这样做了,你也许可以避免更改现有代码,但这似乎不太值得。

这是某人发布的示例: https ://gist.github.com/okapies/2814608

于 2013-06-01T06:11:03.037 回答
1

也许你可以忍受case class Foo(a: Int, b: Int)(val c: Boolean = true)

更新:如果你几乎可以忍受,因为你不想在模式中使用额外的参数,那么你可以做一些简短的工作。

package fooplusplus

case class Foo(a: Int, b: Int) {
  def c: Boolean = true
}
object Foo {
  def apply(a: Int, b: Int, c: Boolean): Foo =
    new {
      private[this] val x = c // http://stackoverflow.com/a/12239654
    } with Foo(a, b) {
      override def c = x
    }
}

object Test extends App {
  def bar(x: Foo) = x match {
    case Foo(1, 2) if !x.c => 3
    case Foo(1, 2)         => 2 // existing code
    case _                 => 1
  }

  Console println bar(Foo(1, 2))
  Console println bar(Foo(1, 2, c = false))
  Console println bar(Foo(0, 2))
}

如果您确实希望与新参数进行模式匹配,这是一种方法:

case class EnhancedFoo(a: Int, b: Int, c: Boolean)
class Foo(a: Int, b: Int, c: Boolean) extends EnhancedFoo(a, b, c)
object Foo {
  def apply(a: Int, b: Int, c: Boolean = true): Foo = new Foo(a, b, c)
  def unapply(x: Foo): Option[(Int, Int)] = Some(x.a, x.b)
}

object Test extends App {
  def bar(x: EnhancedFoo) = x match {
    case EnhancedFoo(1, 2, true) => 3
    case Foo(1, 2)         => 2 // existing code
    case _                 => 1
  }

  Console println bar(Foo(1, 2))
  Console println bar(Foo(1, 2, c = false))
  Console println bar(Foo(0, 2))
}

既然我们还没有做任何真正古怪的事情,那么以下内容如何:

scala> case class Foo(a: Int, b: Int, c: Boolean*)
defined class Foo

scala> import PartialFunction._
import PartialFunction._

scala> val foo = Foo(1,2)
f: Foo = Foo(1,2,WrappedArray())

scala> val goo = Foo(1,2,true)
g: Foo = Foo(1,2,WrappedArray(true))

scala> cond(foo) { case Foo(1,2) => true }
res0: Boolean = true

scala> cond(goo) { case Foo(1,2,false) => true }
res1: Boolean = false

布尔值变为三态,默认为旧式空。

scala> cond(foo) { case Foo(1,2, _ @ _*) => true }
res2: Boolean = true

scala> cond(foo) { case Foo(1,2, x) => true }
res3: Boolean = false

scala> cond(goo) { case Foo(1,2, x @ _*) if x exists identity => true }
res4: Boolean = true
于 2013-06-01T05:25:23.373 回答