在泛型函数中转换数据类型时无效。调用了 toint,但 v 的类型仍然是 double

Invalid when I convert a data type in a generic function. toint is called, but the type of v is still double

我想实现一种将 JSON 数据转换为 Scala 内置对象的方法。代码类似如下:

  implicit class JsonNodeToScala(jsonNode: JsonNode) {

    import scala.jdk.CollectionConverters._
    import scala.reflect.{ClassTag,classTag}

    def toSeq[T: ClassTag]: Seq[Option[T]] = jsonNode.elements.asScala.map(_.toScala[T]).toSeq

    def toMap[T: ClassTag]: Map[String, Option[T]] = jsonNode.fields.asScala.map(field => (field.getKey, field.getValue.toScala[T])).toMap

    def toScala[T: ClassTag]: Option[T] = (this.jsonNode match {
      case v if v.isBoolean => v.asBoolean
      case v if v.isNumber =>
        (v.asDouble, classTag.runtimeClass) match {
          case (v, c) if c == classOf[Int] => 
            print("toInt")
            v.toInt
          case (v, c) if c == classOf[Long] => 
            print("toLong")
            v.toLong
          case (v, _) => v
        }
      case v if v.isTextual => v.asText
      case jsonNode => jsonNode
    }) match {
      case v: T => Some(v)
      case v => 
        println(v, classTag.runtimeClass, v.getClass)
        None
    }
  }

我按如下方式使用时有效

new ObjectMapper().readTree("""[1,2,null,4]""").toSeq[Double]
>>> res136: Seq[Option[Double]] = List(
  Some(value = 1.0),
  Some(value = 2.0),
  None,
  Some(value = 4.0)
)

但是当我传入泛型参数int时,却得不到我想要的结果

new ObjectMapper().readTree("""[1,2,null,4]""").toSeq[Int]
>>>
    toInt(1.0,int,class java.lang.Double)
    toInt(2.0,int,class java.lang.Double)
    (null,int,class com.fasterxml.jackson.databind.node.NullNode)
    toInt(4.0,int,class java.lang.Double)
 
res138: Seq[Option[Int]] = List(None, None, None, None)

结果调用了toint,但是v的类型还是double。是不是Scala处理match case的原因?

如果我想满足我的需求,我应该如何修改我的代码?

"[1, 2, 3]" call toSeq[Int]  => Seq(1, 2, 3)
"[1, 2, 3]" call toSeq[Double]  => Seq(1.0, 2.0, 3.0)

"[1.0, 2.0, 3.0]" call toSeq[Int]  => Seq(1, 2, 3)
"[1.0, 2.0, 3.0]" call toSeq[Double]  => Seq(1.0, 2.0, 3.0)

这里的主要问题是:

case v if v.isNumber =>
  (v.asDouble, classTag.runtimeClass) match {
    case (v, c) if c == classOf[Int] => 
      print("toInt")
      v.toInt
    case (v, c) if c == classOf[Long] => 
      print("toLong")
      v.toLong
    case (v, _) => v
  }

Scala 编译器必须找到 IntLongDouble 之间的共同最低祖先。 在 Scala 2.12 中,它恰好是 Double,因此它再次将 Int 转换为 Double(通过隐式转换)。 欺骗 编译器的一种方法应该是向上转换一些 return 类型,这样它就不能在数值类型之间执行隐式类型转换。例如:

case v if v.isNumber =>
  (v.asDouble, classTag.runtimeClass) match {
    case (v, c) if c == classOf[Int] => 
      print("toInt")
      v.toInt
    case (v, c) if c == classOf[Long] => 
      print("toLong")
      v.toLong
    case (v, _) => (v : Any) // common lowest type now is Any, no conversions will be performed
  }