Scala:直接或通过包装器访问java原语



我知道你可以直接从Scala访问java原语

val javaDouble = new java.lang.Double(1.0)
这是否意味着我们正在通过包装器或直接访问原语 ?语法new java.lang.Double(1.0)看起来像创建一个新对象,因此有一个包装器,它为我们提供了对java原语的访问。如果这是真的,我想知道它会花费多少额外的内存占用和计算。

你说"我知道你可以直接访问Java原语",然后马上给出一个的例子,不是一个Java原语,而是用来包装原语的Java类。

Scala可以访问未装箱的原语——字符串的length方法,例如:

scala> val l = "fish".length
l: Int = 4

这是一个普通的Int(在Java中是int)。没有拳击。

Scala也可以访问Java版本的盒装原语。

scala> val boxed = new java.lang.Integer(2)
boxed: Integer = 2
scala> val isJavaObject = boxed.isInstanceOf[Object]
isJavaObject: Boolean = true

当使用泛型时,根据需要透明地对原语进行装箱和拆箱(类似于Java中的工作方式,但更全面)。它会跟踪你是否期望一个原语,所以看起来一切都"正常工作"。

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

然而,如果你开始模式匹配,你会发现这种错觉只是肤浅的:Int在各种上下文(泛型或强制转换为Any)中被装箱为java.lang.Integer,因为这是JVM的要求。

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)

因此,只要编译器知道正确的类型,它就尽可能使用原语作为原语,并在不使用原语的情况下透明地将它们框起来。如果您忘记了类型,并使用模式匹配来尝试找出那里有什么,它将取出您要求的任何版本。(当它需要自动这样做时,例如使用相等时,它会假设一个盒装原语应该像一个原语一样。)

这是否意味着我们是通过包装器访问原语还是直接访问

在你的例子中,Scala将在内部创建一个java.lang.Double类型的对象,它是AnyRef (Scala中所有引用类型的根类)的子类型。Scala中没有原语。一切都是对象。

这可以被证明:

val javaDouble = new java.lang.Double(1.0)
val x: AnyRef = "hi" 
val y: AnyVal = 2 // AnyVal is the root class of all primitive types
typeOf(javaDouble) <:< typeOf(x)  // true
typeOf(javaDouble) <:< typeOf(y)  // false

最新更新