1

Maps [String、Any]からクラスをインスタンス化しようとしており、いくつかのjson-rpcを介して受信しています。だから私は次の問題になってしまいます:

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

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

キーが存在する場合、すべてOKです。

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が2回呼び出されるという結論に達しました。そして、2回目はIntをAnyとして受け取りました。

質問:

  1. なぜ ?!

  2. これを回避するにはどうすればよいですか?

PS:初心者の質問ならごめんなさい。私は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 に答える