【问题标题】:Add a parameter to a case class with a default将参数添加到具有默认值的案例类
【发布时间】:2013-05-27 23:36:57
【问题描述】:

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

即。我有:

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

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

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

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

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

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

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

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

【问题讨论】:

    标签: scala pattern-matching default-value


    【解决方案1】:

    case Foo 语法不是调用构造函数,而是在object Foo 上调用unapply 的方法。 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

    【讨论】:

      【解决方案2】:

      也许你可以接受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
      

      【讨论】:

      • 我愿意,但不允许任何现有的实例化工作,即。由于error: missing arguments for method apply in object FooFoo(1, 2) 将无法编译。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多