Scala隐式转换调用了两次

时间:2012-07-12 10:18:08

标签: scala implicit-conversion

我正在尝试从Maps [String,Any]实例化类,通过一些json-rpc接收。 所以我最终遇到了以下问题:

val mpa:Map[String, Any] = Map("key"->0.0)

implicit def anyToInt(a:Any):Int = a.asInstanceOf[Double].toInt

当密钥存在时,一切正常。

val i:Int = mpa.getOrElse("key", 0.0)
i: Int = 0

但是当钥匙丢失时......:

scala> val i:Int = mpa.getOrElse("val", 0.0)
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Double
at scala.runtime.BoxesRunTime.unboxToDouble(Unknown Source)
at .anyToInt(<console>:13

现在,如果我们添加一些详细信息:

implicit def anyToInt(a:Any):Int = {
  println(a)
  val b = a.asInstanceOf[Double].toInt
  println("converted")
  b
}

我们得到了:

val i:Int = mpa.getOrElse("val", 0.0)
0.0
converted
0
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Double
     .....

所以我得出结论,anyToInt被调用两次。第二次收到Int as Any。

问题:

  1. 为什么?!

  2. 我该怎么做才能避免这种情况?

  3. P.S。:对不起,如果这是新手问题。我是斯卡拉的新人。

1 个答案:

答案 0 :(得分:1)

隐藏直接转换,特别是对于Any,因为它是你的情况是一个非常糟糕的主意,你已经发现了原因。我建议您只需将输入地图转换为Map[String, Int]即可。你不需要在这里使用implicits,但是如果你仍然想要使用implicits,你应该使用包装器方法:

implicit def anyExtender (x: Any) = new {
  def toInt = x match {
    case x: Double => x.toInt
  }
}

并像这样使用它:

mpa.getOrElse("val", 0.0).toInt

从scala 2.10开始,首选隐式包装器的实现:

implicit class AnyExtender (x: Any) {
  def toInt = x match {
    case x: Double => x.toInt
  }
}

此外,由于您的转化功能不适用于Any的所有子类型,因此使用此类函数扩展Any是不正确的,使用此类函数扩展Double是正确的但它已经拥有它。因此,在您的方案中,使用地图的唯一正确方法如下:

map.getOrElse("val", 0.0).asInstanceOf[Double].toInt

更新

转换为Map[String, Int]对您有用,如果您将想要的逻辑移动到如此隐含的位置:

val resultMapOfStringToIntType = 
  inputMapOfStringToAnyType.mapValues {
    case x: Double => x.toInt
    case x: String => x.toInt
    case x: Timestamp => //...
    // and so on
  }