1

I'm trying to instantiate classes from Maps[String, Any], receiving through some json-rpc. So I end up with following problem:

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

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

When key exists all is OK.

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

But when key is missing ...:

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

Now, if we're add some verbosity as:

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

We got:

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
     .....

So i came to the conclusion that anyToInt is called twice. And the second time it received Int as Any.

Questions:

  1. Why ?!

  2. What should I do to avoid this ?

P.S.: Sorry if it's newbie question. I'm new in scala.

4

1 回答 1

1

隐藏直接转换,尤其Any是在您的情况下,这是一个非常糟糕的主意,您已经发现了原因。我建议您只需将输入映射转换为Map[String, Int]. 您在这里不需要隐式,但如果您仍然想使用隐式,您应该使用包装器方法:

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
  }
于 2012-07-12T10:37:53.267 回答