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

sem*_*ema 4 generics casting scala 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'
  }
}
Run Code Online (Sandbox Code Playgroud)

“某些代码” A1

result val result = f(3)
Run Code Online (Sandbox Code Playgroud)

输出:

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)
Run Code Online (Sandbox Code Playgroud)

“某些代码” A2

println(f(3))
Run Code Online (Sandbox Code Playgroud)

输出:

3.0
Run Code Online (Sandbox Code Playgroud)

“某些代码” A3

println(f(3).getClass)
Run Code Online (Sandbox Code Playgroud)

输出:

int
Run Code Online (Sandbox Code Playgroud)

变体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)
  }
}
Run Code Online (Sandbox Code Playgroud)

输出:

(对于变体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)
Run Code Online (Sandbox Code Playgroud)

dk1*_*k14 5

你可以不投DoubleInteger反正(因为它是盒装)。Numeric只是允许您import num._在T上进行一组数学运算(by )-别无其他(您不需要某种中介类型)。A2之所以有效是因为println所期望Anyf(3),因此T自动推断为Any; println(f[Int](3))永远不会工作,f[Any](3)永远都会工作。

如果要实现操作Doubles的泛型函数(仅当您具有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]
}
Run Code Online (Sandbox Code Playgroud)

您不能只在result.doubleValue.asInstanceOf[A]这里做的原因是A已装箱。@specialized注释不适用于asInstanceOf类型A仍为方框

更新:实际@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]
}
Run Code Online (Sandbox Code Playgroud)

但这在Scala REPL中不起作用 -请当心!

PS清单已弃用:使用classTag/ typeTag代替