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.)
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'
}
}
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)
println(f(3))
Output:
3.0
println(f(3).getClass)
Output:
int
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)
}
}
(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)
Defining a generic classGeneric classes take a type as a parameter within square brackets [] . One convention is to use the letter A as type parameter identifier, though any parameter name may be used. This implementation of a Stack class takes any type A as a parameter.
Examples from the Scala Standard LibraryMost Scala generic classes are collections, such as the immutable List, Queue, Set, Map, or their mutable equivalents, and Stack. Collections are containers of zero or more objects. We also have generic containers that aren't so obvious at first.
The question mark ( ? ) wildcard character can be used to represent an unknown type using generic code. Wildcards can be used with parameters, fields, local variables, and return types.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With