Scala在Java的类型系统上叠加了一个非常优雅的类层次结构,从顶部的Any,到AnyRef和AnyVal,分别覆盖Java的对象和基元,然后最终收敛,将引用类型折叠到Null上,将所有类型折叠到Nothing上。据我所知,没有什么是一切的子类型; Null AnyRef / java.lang.Object的所有子类型的子类型。 [见http://www.scala-lang.org/node/128]
然而,似乎存在一些不规则性,在一些地方,仅仅将所有Scala类型视为无缝类型层次结构的元素是不行的。我觉得这很烦人,想要了解我可能会感到惊讶的地方。
到目前为止,我知道一些违规行为:
1)虽然Null是AnyRef的子类型,但调用null.isInstanceOf [AnyRef](或AnyRef的其他子类型)会返回false。我怀疑这被选择与Java的instanceof运算符的行为一致。
2)无论方差注释如何,一切都与Nothing协变。如果我有一个方法返回一个没有标记协变的类型T,我可以覆盖该方法返回类型Nothing。 [注意:这个说法有误,请参阅下面的答案和评论!]
3)我无法将isInstanceOf应用于AnyVal类型[请参阅Why can AnyVal not be used in an isInstanceOf check?和How to test a value on being AnyVal?]
4)询问是否存在isInstanceOf [Null]这是非法的,这是一个非常必要的问题(虽然不是特别必要,因为“myVar == null”会给出相同的答案)
Scala的类型层次结构中是否存在其他不规则或特殊情况的示例?我觉得这些值得学习和理解,以避免不受欢迎的惊喜。
答案 0 :(得分:7)
1)"A string is a subtype of AnyRef".isInstanceOf[AnyRef]
返回true
。对于AnyRef
的其他子类型也是如此,除了Null
。正如你所说,唯一不正常的做法是与Java保持一致。
2)如果B
是A
的子类型,即B <: A
,那么您始终可以覆盖方法:
def foo: A = ...
为:
override def foo: B = ...
这称为改进返回类型,并且始终允许。由于Nothing
是所有其他类型的子类型(Nothing <: A
所有A
),因此您始终可以将返回类型细化为Nothing
(例如,通过在正文中抛出异常)方法)。这是一个非常规则的财产。返回类型协方差与variance annotations on type parameters没有直接关系。
3)其他问题很好地涵盖了这一点。
4)这是因为Java运行时中不存在Null
类型。我想如果你想模仿这个,你可以创建自己的instanceOf
方法 - 你首先要检查参数是否为null
,否则,进行正常的isInstanceOf
检查。 / p>
还有其他违规行为,是的。例如,请参阅:If an Int can't be null, what does null.asInstanceOf[Int] mean?
数组是另一个示例,您可以在运行时使用装箱/取消装箱或instanceof
检查来支付通用阵列的一致性。将new Array[Any]
转换为对象数组 - 将整数存储到数组中将导致对其进行装箱。无论何时使用Array[T]
,其中T
没有上限,每次索引元素时,数组都将与正确的运行时数组类型进行图案匹配。
为了更好地理解你可能会感到惊讶,从这些构造如何被转换为JVM的角度来看是有用的,其中有一个原始和引用类型的概念,装箱/拆箱和不同的数组类。