8

我希望能够做到这一点:

scala> val Int(i) = "1"
i: Int = 1

但是Int没有unapply方法。

我找到了这个答案,它提供了有关如何向现有对象隐式添加方法的说明,所以我试了一下。他们提供的解决方案有效,但不幸的是不适用于模式匹配。这是我所拥有的:

object UnapplyInt {
  val IntRE = """^(\d+)$""".r
  def unapply(v: String): Option[Int] = v match {
    case IntRE(s) => Some(s.toInt)
    case _ => None
  }
}
implicit def int2unapplyInt(objA: Int.type) = UnapplyInt

这些测试用例都很好:

val UnapplyInt(i) = "1"       // pattern matching with unapply is fine
val i = Int.unapply("1").get  // implicit conversion is fine

但我想要的失败了:

scala> val Int(i) = "1"
<console>:10: error: object Int is not a case class constructor, nor does it have an unapply/unapplySeq method
       val Int(i) = "1"
           ^

如果隐式转换有效并且模式匹配unapply有效,为什么Scala不将这两个东西放在一起进行隐式模式匹配呢?

4

1 回答 1

8

编辑所以我原来的推理不好。真正的原因来自Scala 语言规范的第 8.1.8 节

Syntax:
    SimplePattern ::= StableId ‘(’ [Patterns] ‘)’

也就是说,提取器对象必须是稳定的,而隐式转换是不稳定的。没有解释为什么提取器必须稳定;我怀疑这是因为 Scala 不想将提取器视为表达式,因为这很快就会变得模棱两可:

... match {
    foo(bar)(baz)
}

现在哪个是构造函数,哪个是模式变量?

幸运的是你可以做到这一点,它工作得很好(虽然,正如你所评论的,引入了其他问题):

object Int {
    def unapply(v: String) = try Some(v.toInt)
        catch { case _: NumberFormatException => None }
}

val Int(i) = "5"

因为类型Int和对象Int位于不同的命名空间中。

于 2012-03-15T20:29:53.533 回答