Scala模式匹配多种类型

4
我有一个从JValue中提取Int的代码,它应该对多个JValue子类完全相同,所以我试图避免重复。但是,如下所示,scala认为j是泛型JValue,并且j.values返回类型为Values的值,这显然没有isValidInttoInt方法。
jvalue \ name match {
  case j @ (JInt | JDecimal | JDouble) => {
    val num = j.values
    if (num.isValidInt) num.toInt.success else reportError(name + " is not a valid int")
  }

问题在于如何避免重复?我不太喜欢将 JValue 转换为字符串,因为这段代码处理的是刚从字符串解析成 AST 的 json。我开始考虑为我需要匹配的三种类型编写包装器,并从这些类型中隐式转换到包装器中,然后创建一个超类,专门用于这些包装器作为模式使用,但我不确定如何实现。
是的,我知道这里有多个类似的问题(例如这个这个),但它们中每一个最多只包含部分解决方案。
3个回答

3

实现一个提取器来从任意的 JSON 值中获取 Int 值,然后在模式匹配中使用该提取器。

object JsonInt {
  def unapply(json: JValue): Option[Int] = json match {
    case JInt(i) if i.isValidInt => Some(i.toInt)
    case JDecimal(d) if d.isValidInt => Some(d.toInt)
    case JDouble(d) if d.isValidInt => Some(d.toInt)
    case _ => None
  }
}

jvalue \ name match {
  case JsonInt(num) => num.success
  case _ => reportError(s"$name is not a valid int")
}

乍一看,这似乎是一个干净的解决方案,但实际上它看起来更像case JInt(i) => if (i.isValidInt) Some(i.toInt) else None //JInt's i is a BigInt 对于JDecimal和JDouble也是同样的代码,这正是我想要避免的。 - Yar
2
我已更新我的答案,包括isValidInt检查,这可能比您所述的更简洁。另一个问题:您使用的Json库是什么?\语法看起来很熟悉,适用于lift-json,它具有extractOpt[T]方法,您可以使用该方法直接从任何JValue中提取Option[Int] - Dylan
查看了 extract[T] 方法。它可以将任何数字值转换为 Int(即 extract[Int]),唯一的问题是它不关心截断,例如 2.1 会在没有错误的情况下转换为 2。如果我要编写自己的要关心截断的 Formats ,那么它将涉及与我们这里一样的程度的繁琐操作。 - Yar

1
Scala对结构类型的支持有限。似乎JValue是这三种类型的最低公共祖先。如果需要,您可以通过定义从JValue到某个包装类的隐式转换来绕过它,该包装类将具有isValidInt方法。

1
如果您不介意使用结构类型,可以使用asInstanceOf方法来使用toIntisValidInt方法:
type IntExt = {

  def toInt: Intdef isValidInt: Boolean

}
jvalue \ name match {

  case j @ (_: JInt | _: JDecimal | _: JDouble) =>
       
    val num = j.values.asInstanceOf[IntExt]

    if (num.isValidInt) num.toInt.success else reportError(name + " is not a valid int")
}

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