You say "I know you can access Java primitives directly" and then follow immediately with an example of not a Java primitive but the Java class used to box a primitive.
Scala can access unboxed primitives--the length
method on strings, for instance:
scala> val l = "fish".length
l: Int = 4
This is a just plain Int
(int
in Java). No boxing in sight.
Scala can also access Java's version of boxed primitives.
scala> val boxed = new java.lang.Integer(2)
boxed: Integer = 2
scala> val isJavaObject = boxed.isInstanceOf[Object]
isJavaObject: Boolean = true
When using generics, primitives are transparently boxed and unboxed as needed (similar to how it works in Java, but a bit more comprehensively). It keeps track of whether you expect a primitive or not, so it looks like everything "just works".
scala> def ident[A](a: A): A = a
ident: [A](a: A)A
scala> def lAgain = ident(l)
lAgain: Int
scala> def boxedAgain = ident(boxed)
boxedAgain: Integer
However, if you start pattern matching, you will find that the illusion is only skin-deep: Int
is boxed to java.lang.Integer
in a variety of contexts (generics or a cast to Any
) because that is a JVM requirement.
scala> def isInt(a: Any) = a match { case i: Int => true; case _ => false }
isInt: (a: Any)Boolean
scala> val test = (isInt(l), isInt(boxed))
test: (Boolean, Boolean) = (true,true)
scala> def isBoxedInt(a: Any) = a match { case _: java.lang.Integer => true; case _ => false }
isBoxedInt: (a: Any)Boolean
scala> val test2 = (isBoxedInt(l), isBoxedInt(boxed))
test2: (Boolean, Boolean) = (true,true)
So, as long as the compiler knows the right type, it uses primitives as primitives whenever possible, and boxes them transparently whenever not. If you lose track of the type and use pattern matching to try to figure out what's there, it will pull out whichever version you ask for. (When it needs to do so automatically, e.g. with equality, it will assume a boxed primitive is supposed to act like a primitive.)