Scala转换为泛型类型(用于通用数值函数) [英] Scala cast to generic type (for generic numerical function)

查看:1932
本文介绍了Scala转换为泛型类型(用于通用数值函数)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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





变式A



 套件测试

object mytest {
def f [A](x:A)(隐式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)

p>

 线程main中的异常java.lang.ClassCastException:java.lang.Double不能转换为java.lang.Integer 
在test.mytest $ .main(test.scala:10)
at test.mytest.main(test.scala)
at test.mytest.main(test.scala)



'一些代码'A2



  println(f(3))

输出:

  3.0 



'a some code'A3



  println(f(3).getClass)
/ pre>

输出:

  int 



变式B



 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 Array [String]){
val result = f(3)
}
}


b $ b

输出:



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

 线程main中的异常java.lang.ClassCastException:无法将java.lang.Double转换为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)


方案

无论如何,你不能将 Double 转换为整数 数字只是允许你在你的T上有一组数学运算( import num ._ (你不需要一些中介类型)。 A2只有因为println期望任何作为 f(3)的结果,所以 T 会自动推断为任何; println(f [Int](3))将永远不会工作, f [Any](3)



如果你想实现通用功能操作双打(你可能需要只有当你有操作特定于双) - 你应该更好地返回双。如果你不能 - 你必须通过分析T来手动构造类型:

  def f [A] A)(隐式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 注释不适用于 asInstanceOf 类型A仍然有框



UPDATE:actual @specialized works:

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

result.doubleValue.asInstanceOf [A]
}

但它在Scala REPL 中不起作用 - 请小心!



PS清单已弃用:请使用 classTag / typeTag 而不是


I'm trying to implement a generic function that wraps a mathematical Java function. For simplicity, we can assume that the Java function (Java 7) takes one parameter and returns a result, both of type java.lang.Double. Of course, the wrapper function should take a parameter and a result, both of generic but numeric type A. The problem is that I'm not able to cast the result back to type A in the wrapper function. Where/what is the problem?

Note: (I'm a newbie on Scala and used the following reference to solve the problem.)

Variant 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'
  }
}

'Some code' A1

result val result = f(3)

Output:

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)

'Some code' A2

println(f(3))

Output:

3.0

'Some code' A3

println(f(3).getClass)

Output:

int

Variant 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)
  }
}

Output:

(This is the same also for equivalents of variants A1, A2, and A3, because the exception is now thrown in line 6 of function f.)

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)

解决方案

You can't cast Double to Integer anyway (because it's boxed). Numeric just allows you to have a set of mathematical operations (by import num._) on your T - nothing else (you don't need some mediate type for that). A2 works only because println expecting Any as a result of f(3), so T is automatically inferred to Any; println(f[Int](3)) will never work, f[Any](3) will always work.

If you want to implement generic function which operates Doubles (you may need that only if you have operations specific to Double) - you should better return Double. If you can't - you will have to construct the type manually by analizing 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]
}

The reason why you can't just do result.doubleValue.asInstanceOf[A] here is that A is already boxed. @specialized annotation doesn't work for asInstanceOf (type A is still boxed)

UPDATE: actually @specialized works:

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]
}

But it doesn't work in Scala REPL - so be careful!

P.S. Manifests are deprecated: use classTag/typeTag instead

这篇关于Scala转换为泛型类型(用于通用数值函数)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆