Scala强制转换为泛型类型(用于通用数值函数)

时间:2014-11-30 11:36:12

标签: scala generics casting scala-java-interop

我试图实现一个包含数学Java函数的泛型函数。为简单起见,我们可以假设Java函数(Java 7)接受一个参数并返回结果,类型都是java.lang.Double。当然,包装器函数应该采用通用但数字类型A的参数和结果。问题是我无法将结果转换回包装函数中的类型A.哪里/有什么问题?

注意:(我是Scala的新手并使用以下参考来解决问题。)

变体A

package test

object mytest {
  def f[A](x: A)(implicit num: Numeric[A]): A = {
    val result = new java.lang.Double(num.toDouble(x))
    result.asInstanceOf[A]
  }

  def main(args: Array[String]) {
    // 'Some code'
  }
}

'一些代码' A1

result val result = f(3)

输出:

Exception in thread "main" java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Integer
    at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:105)
    at test.mytest$.main(test.scala:10)
    at test.mytest.main(test.scala)

'一些代码' A2

println(f(3))

输出:

3.0

'一些代码' A3

println(f(3).getClass)

输出:

int

变体B

package test

object mytest {
  def f[A : Manifest](x: A)(implicit num: Numeric[A]): A = {
    val result = new java.lang.Double(num.toDouble(x))
    manifest[A].erasure.cast(result).asInstanceOf[A]
  }

  def main(args: Array[String]) {
    val result = f(3)
  }
}

输出:

(对于变体A1,A2和A3的等价物也是如此,因为异常现在在函数f的第6行中抛出。)

Exception in thread "main" java.lang.ClassCastException: Cannot cast java.lang.Double to int
    at java.lang.Class.cast(Class.java:3176)
    at test.mytest$.f(test.scala:6)
    at test.mytest$.main(test.scala:10)
    at test.mytest.main(test.scala)

1 个答案:

答案 0 :(得分:4)

无论如何,您无法将Double投放到Integer(因为它是盒装的)。 Numeric只允许你对你的T进行一系列数学运算(import num._) - 没有别的(你不需要一些中介类型)。 A2仅因为println因Any而期待f(3)而有效,因此T会自动推断为Any; println(f[Int](3))永远不会有效,f[Any](3)将永远有效。

如果你想实现操作双打的通用功能(只有当你有特定于Double的操作时才需要它) - 你应该最好返回Double。如果你不能 - 你必须通过分析T:

手动构建类型
def f[A](x: A)(implicit num: Numeric[A]): A = {
   val result = new java.lang.Double(num.toDouble(x))
   (x match {
       case x: Double => result
       case x: Int => result.toInt
       case x: Float => result.toFloat
       case x: Long => result.toLong
   }).asInstanceOf[A]
}

你不能在这里做result.doubleValue.asInstanceOf[A]的原因是A已经装箱了。 @specialized注释不适用于asInstanceOftype A is still boxed

更新:实际上@specialized有效:

def f[@specialized(Int, Double, Long, Float) A](x: A)(implicit num: Numeric[A]): A = {
   val result = new java.lang.Double(num.toDouble(x)) 

   result.doubleValue.asInstanceOf[A]
}

但它doesn't work in Scala REPL - 所以要小心!

P.S。不推荐使用清单:使用classTag / typeTag代替