Hissashi Rocha
Hissashi Rocha

Reputation: 63

Unwanted conversion from Scala AnyVal types to Java Types

I'm writing a function that get some parameters and extract their types, but whenever this parameters are under Scala AnyVal (Int, Long, Double, ...) they get converted to Java Types (Integer, ...).

Here a small example of that function:

def functionX(args: Any*) = {
   args.map(a => a.getClass)
}

val num: Int = 10

println(num1.getClass) // prints int

println(functionX(num1)) // prints Integer

What should I do to have all parameters types, whether they're Int or Integer, in their original types and not converted?

I also realized that if a variable is put inside a Scala collection, it also gets converted to Java types:

val num: Int = 1
val numList = List(num)

println(num.getClass) // prints int
println(numList.head.getClass) // prints Integer

Is there any Java autoboxing happening? But aren't Scala types, like Int, under AnyVal class and nor primitive types?


To give more context on why I'm trying to do this, here is an example:

trait MyTrait {
  def myTraitMethod : String
}

object MyObj extends MyTrait {
  def myTraitMethod = "Works fine"
  def myObjMethod(num1: Int, num2: Int) = num1 * num2
}

case object Manager {
  def currentObj: MyTrait = MyObj
}

val result = Manager.currentObj.myObjMethod
// I wanted to access this object method that is not in Trait, but
// it doesn't seem possible.

Instead of doing the last line of the code above, I'm using Reflection to do something like this:

def executeMethod(obj: MyTrait, methodName: String, args: Any*) = {
    val argTypes = args.map(t => t.getClass)
    val argsObjects = args.map(o => o.asInstanceOf[Object])

    val method = obj.getClass.getMethod(methodName, argTypes:_*)

    method.invoke(obj, argsObjects:_*)

}

val num = 10
val result = executeMethod(Manager.currentObj, "myObjMethod", num1, num2)
// That's where I have problem with Int being converted to Integer
// and the method is not found because it's (Int, Int) and not (Integer, Integer)

Upvotes: 0

Views: 189

Answers (1)

Willis Blackburn
Willis Blackburn

Reputation: 8204

functionX is a function that takes a variable number of arguments of type Any.

At some point, Scala is going to have to compile this into a function that can run on the JVM. That function is going to take one argument, which will be an array or sequence type, because that's how the JVM implements variable arguments. In fact, it's going to be scala.collection.immutable.ArraySeq.

You probably understand that JVM primitive types are not objects. You can't have a single variable that can either hold a primitive value like int or an object like String. The JVM includes "box" types Integer, Double, etc., so you can use primitive values as objects when necessary.

ArraySeq has to be an collection of something, and since in the case of functionX you've said that it can hold anything, at the JVM level, the something has to be Object. Since it's an ArraySeq of Object, any primitive values that you want to put into it will have to be boxed.

All the behavior you observe will make sense once you realize that Scala's Int type is the JVM's primitive int type. They're not really objects.

In order to invoke the right method through reflection, you could just assume that if an argument is of type Integer, then you should use the type Integer.TYPE or classOf[int] for the purpose of looking up the method. Of course that will fail if the method you want actually takes Integer. Once you find the right method, the JVM will take care of unboxing any boxed values in your argument list.

Upvotes: 2

Related Questions