Scala中的Any、Nothing、Null、Nil
Any
在scala中,Any类是所有类的超类。Any有两个子类:AnyVal和AnyRef。
对于直接类型的scala封装类,如Int、Double等,AnyVal是它们的基类;对于引用类型,AnyRef是它们的基类。
Any是一个抽象类,它有如下方法:!=()、==()、asInstanceOf()、equals()、hashCode()、isInstanceOf()和toString()。
AnyVal没有更多的方法了。AnyRef则包含了Java的Object类的一些方法,比如notify()、wait()和finalize()。
AnyRef是可以直接当做java的Object来用的。对于Any和AnyVal,只有在编译的时候,scala编译器才会将它们视为Object。换句话说,在编译阶段Any和AnyVal会被类型擦除为Object。
Nothing
如上图所示,Nothing是所有类的子类,是一个类。Nothing没有对象,但是可以用来定义类型。
在Java当中你很难找到类似的东西对应Scala中的Nothing。Nothing到底是什么呢?或者换个方向考虑:Nothing的用处是什么呢?
用于标记不正常的停止或者中断
一个空的collection
scala> def foo = throw new RuntimeException
foo: Nothing
scala> val l:List[Int] = List[Nothing]()
l: List[Int] = List()
因为List[+A]定义是协变的,所以List[Nothing]是List[Int]的子类,但List[Null]不是List[Int]的子类
Null
Null是所有AnyRef的子类,是所有继承了Object的类的子类,所以Null可以赋值给所有的引用类型(AnyRef),不能赋值给值类型(AnyVal),这个java的语义是相同的。null是Null的唯一对象。
val x = null // x: Null
val y: String = null // y: String = null
val z: Int = null // error: type mismatch
val u: Unit = null // u: Unit = ()
1
2
3
4
注意,不要被Unit值类型在赋值时的障眼法给骗了,以为null可以被赋给Unit。实际上把任何表达式结果不是Unit类型的表达式赋给Unit类型变量,都被转为{ expr; () },所以上面的等同于{null; ()}把最终得到的()赋给了u变量。
Null在类型推导时只能被造型为AnyRef分支下的类型,不能造型为AnyVal分支下的类型,不过我们显式的通过asInstanceOf操作却又是可以把null造型为AnyVal类型的:
val i = null.asInstanceOf[Int]
// 类似于java里的 int i = (int)((Integer)null);
先装箱Int为引用类型,null被造型成了引用类型的Int(java.lang.Integer),然后又做了一次拆箱,把一个为null的Integer类型变量造型成Int值类型,但在拆箱这一点处理上,体现了scala与java的不同:
// java里,编译通过,运行失败,空指针异常
int i = (int)((Integer)null);
// scala里,把值为null的Integer拆箱为值类型Int是ok的,得到Int的默认值0
val i = null.asInstanceOf[java.lang.Integer].asInstanceOf[Int]
在java里基本类型(primitive type) 与引用类型是有明确差异的,虽然提供了自动装箱拆箱的便捷,但在类型上两者是不统一的;而scala里修正这一点,Int类型不再区分int/Integer,类型一致,所以值为null的Integer在通过asInstanceOf[Int]时被当作一个未初始化的Int对待,返回了一个默认值Int(注:asInstanceOf不改变原值,返回一个新值)。
Nil
Nil是一个空的List,定义为List[Nothing],根据List的定义List[+A],所有Nil是所有List[T]的子类。