为啥 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]
,然后根据这种类型解释value1
、value2
等等(斯卡拉Double
)。如果您将类型显式声明为Seq[Any]
,则不会运行类型推断(因为您自己给出了类型),因此不会“强制”将值value1
、value2
等解释为固定值输入。
由于Seq
是一个集合,因此不允许使用基元并且必须自动装箱,因此Java 的double
不能容纳而java.lang.Double
可以。试图隐藏Seq[Double]
的装箱和拆箱并透明地交换原语和对象的逻辑没有发挥作用。实际上,在Seq[Any]
中,每个元素可能属于不同的类型,这意味着这种装箱和拆箱在一般情况下不起作用(在您的示例中,res0(0).getClass
是 Integer
,而 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 对象?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 sbt 为 import scala.swing 给出“对象 swing 不是包 scala 的成员”?