Scala中的案例类、模式匹配和柯里化构造函数

14

它们似乎不太混合:

abstract class A
case class B (var a: Int)(var b: String) extends A
case class C extends A
以下方法是行不通的:
B(1)("1") match {
  case B(a)(b) => print("B")
  case C() => print("C")
}

问题在于模式匹配和柯里化参数似乎不起作用。有没有解决这个问题的方法?

3个回答

14
如果你查看类B中创建的unapply函数的签名,你会发现它是:unapply(x$0: Q): Option[Int]。因此,unapply函数与case类的第一组参数一起工作。
Scala规范(§5.3.2)证实了这一点:
“在案例类的第一个参数部分中,形式参数被称为元素;它们受到特殊处理。首先,此类参数的值可以作为构造模式的字段被提取。”
明确表示只有第一组参数可通过提取器使用。
几种解决方法:
  • 展开参数
  • 如果要测试两个值,请使用带有保护条件的模式匹配:case x@B(3) if x.b == "bazinga" => ...
  • 使用普通类并定义自己的伴生对象,其中包含自己的apply/unapply

7
这有什么问题吗?
def m(a: A) = a match {
  case b: B => print("B")
  case c: C => print("C")
}

我之所以问这个问题,是因为你没有要求比这更多的功能。

编辑

这可能会有所帮助:

object Dog {
   def apply(name: String)(size: Int) = new Dog(name)(size)
   def unapply(dog: Dog) = Some(dog.name, dog.size)
}
class Dog(val name: String)(var size: Int)

现在您可以像这样创建狗:

new Dog("Snoopy")(10) 

或者像这样:
Dog("Snoopy")(10)

但是,当你在狗上进行模式匹配时,构造函数模式不是柯里化的。

Dog("Snoopy")(10) match {
   case Dog(a, b) => // do sth with a or b
}

你的第一个例子可以工作,但是在case语句中我无法访问B.a和B.b,除非进行一些丑陋的类型转换。此外,在你的编辑中,我不确定为什么构造函数模式没有柯里化。是因为unapply吗? - Henry Henrinson
说实话,我不知道它为什么能工作。我是通过试错偶然发现的。它肯定在Scala规范中某处有提到。如果在你的情况下相关的话,你可能需要查一下。 - agilesteel
1
是的,在 case 语句中使用的模式是由 unapply 函数的结果给出的。它永远不能被柯里化。在 Scala 规范中对应的部分是 §8.1.8。 - Nicolas
确实,这就是unapply函数。如果您不想阅读规范,这里有一个更易读的资源:http://www.scala-lang.org/node/112 - Henry Henrinson

2

您可以使用普通的case类,并定义一个带有多个参数列表的工厂方法。


也许我们可以补充说明,如果工厂方法在伴生对象中声明(因为它将具有与由于 case 类声明的方法相同的擦除),则不能将其称为 apply - Nicolas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接