为啥 Scala 偶尔会退回到 Java 对象?

Posted

技术标签:

【中文标题】为啥 Scala 偶尔会退回到 Java 对象?【英文标题】:Why does Scala occasionally fall back to Java objects?为什么 Scala 偶尔会退回到 Java 对象? 【发布时间】:2013-08-27 19:28:12 【问题描述】:

我几乎可以肯定以前有人问过这个问题,但我找不到合适的词来找到它。

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

为什么 Scala 在Seq[Any] 中将我的Double 输入作为java.lang.Double 处理,但在使用Seq[AnyRef] 时将其保持为scala.Double?有没有办法防止这种行为,而是始终使用 Scala 类型?

【问题讨论】:

【参考方案1】:

Scala 的 Double 对应于 Java 的 double,但如果需要,它可能会自动装箱和拆箱,当它自动装箱时它会变成 java.lang.Double。在实践中,集合需要对原始变量进行自动装箱。

如果没有显式声明类型,则根据分配给它们的值推断您声明的集合类型。问题中两个声明之间的区别在于,对于Seq(value1,value2,...),类型推断试图找到“最佳”类型,提出Seq[Double],然后根据这种类型解释value1value2等等(斯卡拉Double)。如果您将类型显式声明为Seq[Any],则不会运行类型推断(因为您自己给出了类型),因此不会“强制”将值value1value2 等解释为固定值输入。

由于Seq 是一个集合,因此不允许使用基元并且必须自动装箱,因此Java 的double 不能容纳而java.lang.Double 可以。试图隐藏Seq[Double] 的装箱和拆箱并透明地交换原语和对象的逻辑没有发挥作用。实际上,在Seq[Any] 中,每个元素可能属于不同的类型,这意味着这种装箱和拆箱在一般情况下不起作用(在您的示例中,res0(0).getClassInteger,而 res2(0).getClass是双)。

因此,本质上,如果您没有显式声明类型,则类型推断会启动并首先尝试为集合的所有元素找到一个公共类型,然后在使用集合类型参数时将所有元素转换为该类型明确指定,不会发生这样的事情,并且所有值的类型都被解释为“原始”。

【讨论】:

但文字,例如3,是Int <: AnyVal <: Any。我一直要求的是Seq[Any]。所以乍一看,维护 Scala 类型应该不是问题。为什么拳击变得必要? @Taig Boxing 是必需的,因为集合适用于对象。 Scala 试图隐藏存在原始类型和对象的事实,但在 JVM 级别确实存在这种区别。由于 Scala 在 JVM 上运行,因此受到此限制。它试图隐藏它,但并非总是如此。 JVM 没有 AnyVal 的等价物,所以在集合内部,层次结构看起来像 java.lang.Integer <: Object【参考方案2】:

正在发生的事情是拳击。因为第一个是Seq[Any],所以其中的任何原语都将以盒装形式返回。第二种情况,因为类型是Seq[Double],所以对成员的访问会被自动拆箱(但仍会被装箱存储在Seq中)。

试图获取原语的运行时类必然会导致各种问题。尽量不用getClass

【讨论】:

以上是关于为啥 Scala 偶尔会退回到 Java 对象?的主要内容,如果未能解决你的问题,请参考以下文章

scala设计者为啥要提供package object

为啥kafka 用scala

为啥 sbt 为 import scala.swing 给出“对象 swing 不是包 scala 的成员”?

偶尔会遇到长时间的垃圾收集延迟,为啥?

Scala 性能:为啥这个 Scala 应用程序比同等的 Java 应用程序慢 30 倍?

为啥scala maven工件对每个scala版本都有一个工件,而不是每个scala版本都有一个分类器?