【问题标题】:Scala cast to generic type (for generic numerical function)Scala 转换为泛型类型(用于泛型数值函数)
【发布时间】:2014-11-30 11:36:11
【问题描述】:

我正在尝试实现一个包含数学 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)

【问题讨论】:

    标签: scala generics casting scala-java-interop


    【解决方案1】:

    无论如何,您都不能将Double 转换为Integer(因为它被装箱了)。 Numeric 只允许你在你的 T 上进行一组数学运算(import num._)——没有别的(你不需要一些中介类型)。 A2 之所以有效,是因为 println 期望 Any 作为 f(3) 的结果,所以 T 会自动推断为 Anyprintln(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]
    }
    

    你不能在这里只做result.doubleValue.asInstanceOf[A]的原因是A已经被装箱了。 @specialized 注解不适用于asInstanceOf (type 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 - 所以要小心!

    附:不推荐使用清单:请改用classTag/typeTag

    【讨论】:

    • 没有任何可能像在 Java 中那样强制将 Double 转换为 Integer,例如(int)(1.8) 返回 1?尽管如此,匹配输入参数对我来说是有效的,但它有点像一个 hack 并且 - 从我的角度来看 - 得到语言本身的支持会很好,因为这样 Variant B 会起作用,不是吗?非常感谢,对我来说现在可以了! :)
    • (1.8).asInstanceOf[Int] 在 scala 中有效,但不适用于盒装类型(即使在 Java 中)
    • 太好了!更新后的解决方案现在使用语言功能,因为我希望一种语言可以工作:该解决方案现在“真正”通用,因为以前的解决方案对于函数 def f[A](list: List[A])(implicit num: Numeric[A]): A = { ... } 不起作用(或至少需要另一种解决方法)如果列表为空。谢谢! +1 为您更新的解决方案。
    猜你喜欢
    • 2020-06-16
    • 2021-10-13
    • 2021-12-01
    • 2021-02-21
    • 1970-01-01
    • 1970-01-01
    • 2013-11-15
    • 1970-01-01
    • 2020-03-14
    相关资源
    最近更新 更多