Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala cast to generic type (for generic numerical function)

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.)

  • I'm using implicit type trait Numeric to enforce that template types are numeric. How do I implement a generic mathematical function in Scala
  • I found 'import scala.collection.JavaConversions._' which I do not fully understand, but it seems to me that it's rather for conversion of Java collections and doesn't solve my problem, does it? Iterating over Java collections in Scala
  • As described here, I tried to use Manifest to enforce correct casting (Variant B) which, however, ends up in an exception java.lang.ClassCastException. Scala asInstanceOf with parameterized types

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)
like image 515
sema Avatar asked Nov 30 '14 11:11

sema


People also ask

How do I use generic in 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.

Does Scala support generics?

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.

Which character is used for generic type?

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.


1 Answers

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

like image 74
dk14 Avatar answered Sep 25 '22 19:09

dk14