我正在尝试从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。
问题:
为什么?!
我该怎么做才能避免这种情况?
P.S。:对不起,如果这是新手问题。我是斯卡拉的新人。
答案 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
}