Why does Scala occasionally fall back to Java objects?

235 views Asked by At

I'm almost certain that this question has been asked before, but I'm missing the right words to find it.

scala> Seq[Any]( 3, 3.4 )
res0: Seq[Any] = List(3, 3.4)

scala> res0( 1 ).getClass
res1: Class[_] = class java.lang.Double

scala> Seq( 3, 3.4 )
res2: Seq[Double] = List(3.0, 3.4)

scala> res2( 1 ).getClass
res3: Class[Double] = double

Why is Scala handling my Double input as java.lang.Double within a Seq[Any] but keeps it as scala.Double when working with a Seq[AnyRef]? Is there a way to prevent this behavior and instead always use the Scala types?

2

There are 2 answers

2
Michał Kosmulski On BEST ANSWER

Scala's Double corresponds to Java's double but may get autoboxed and unboxed if needed and it becomes java.lang.Double when it gets autoboxed. In practice, collections require autoboxing of primitive variables.

The types of collections you declare get inferred based on the value assigned to them if the type is not declared explicitly. The difference between the two declarations in the question is that for Seq(value1,value2,...) type inferrence tries to find the "best" type, comes up with Seq[Double] and then inteprets value1, value2 and so on based on this type (Scala Double). If you declare the type explicitly as Seq[Any], type inferrence is not run (since you gave the type yourself) and so the values value1, value2 etc. are not "forced" into being interpreted as being of a fixed type.

Since Seq is a collection, primitives are not allowed and must be autoboxed, so Java's double can't fit in while java.lang.Double can. The logic which tries to hide boxing and unboxing for Seq[Double] and transparently interchanges the primitive and the object doesn't come into play. Actually, in a Seq[Any], each element may be of a different type which means such boxing and unboxing can't work in the general case (in your example, res0(0).getClass is an Integer in contrast to res2(0).getClass which is a Double).

So, essentially, if you don't declare the type explicitly, the type inferrence kicks in and first tries to find a common type for all elements of the collection and then casts all elements into that type while with the collection type parameter specified explicitly, no such thing takes place and all values' types are interpreted "raw".

0
Daniel C. Sobral On

What is happening is boxing. Because the first is a Seq[Any], any primitives in it will come back in the boxed form. In the second case, because the type is Seq[Double], access to the members will be automatically unboxed (but will still be stored boxed in the Seq).

Trying to get the runtime class for primitives is bound to cause all sorts of problems. Try not needing to use getClass.