Understanding class of integers

Can someone help me understand what is happening here.

In the worksheet, 3 prints as res4: Int = 3, so far so good.
Next 3.getClass prints as res5: Class[Int] = int,
and 3.getClass().isInstance(3) prints as res6: Boolean = false.

It is really the case that 3 is not an instance of its own class?

TL;DR; the problem is that the real class of Ints is java.lang.Integer and not Scala.Int.

The reason this is happening is because (as the javadoc shows) isInstance accepts an Object: as such the primitive value 3 has to be boxed into a java.lang.Integer.

1 Like

Scala works hard to unify primitive types and reference types at the language level, but once you drop down to the Java / JVM / bytecode / reflection level, that niceness disappears and you must always be aware of the difference.

Note that Class[Int] isn’t even a valid Java/JVM type — Java generics don’t allow primitive types as type parameters.

2 Likes

So how does the Scala pattern matcher decide whether an object is an integer (sorry object is the wrong word here but I don’t know the correct word.)

List(1, 1.0, "hello").map {
  case i:Int => "integer"
  case d:Double => "double"
  case s:String => "string"
  case _ => "other"  
}
--> List("integer", "double", "string")

There’s auto-boxing at play. If you store a primitive into a collection, it is automatically boxed (in bytecode scala.runtime.BoxesRunTime.boxToInteger and scala.runtime.BoxesRunTime.boxToDouble is called for your code). So, at runtime the 1 int literal is really a java.lang.Integer inside the List and the compiler generates instanceof checks for the case clause matching for java.lang.Integer. It’s magic.

I wonder whether the semantics of “inside a List” is something the programmer can understand, or whether it’s subject to optimization. For example, in the code below, x is an element of a List, is it thus dependably boxed? It appears the elements of this List are susceptible to isInstance. This seems to also work for Array, Vector, and Seq.

List("hello",1,2,3.0,4.0).filter{
  x => classOf[java.lang.Integer].isInstance(x)
}
--> List(1, 2)

Java collections only store objects, and primitive types are no objects. Scala just inherits these restrictions and tries to cope with them (and improve on the situation).

You can only store primitive types into Arrays, which is reflected at runtime, since there are specialised array types for each primitive:

$ Array[Int](1, 2, 3).getClass.getName
val res0: String = [I  // <- represents array of primitive ints

Sometimes you can avoid boxing for your own parametrized classes by using @specialized.

Here’s a good article about it: https://scalac.io/specialized-generics-object-instantiation/

2 Likes

Actually object is the correct word.

First, from the language point of view every value is an object.
Second, from the runtime point of view everything generic is erased to object.

Yes, that seems right.

classOf[java.lang.Integer].isInstance(3)
--> true