Prologue
Scala语言虽然是构建在JVM体系之上的,但为了适应函数式编程的需要,它的语法和Java几乎完全不同,在很多基础层面——比如类型系统——也是自成一派的。在Scala类型系统中,Null、Nothing、Nil、None、Unit这些类型看起来似乎都表达“空”的语义,但实际上很有一些区别,容易混淆。本文来简单分辨一下。
作为预热,可以先看看来自官方文档的class hierarchy diagram。
Null
Scala中所有类型的父类型是Any,Any有两个一级子类型:AnyVal和AnyRef,分别对应所有值类型(primitives)和引用类型(references)的根。Null是所有AnyRef的子类,处于Scala类型体系的次底层。并且它只有一个实例,就是null,表示空引用。
在Scala源代码中,Null作为一个空的特征(trait)存在。
package scala
/** `Null` is - together with [[scala.Nothing]] - at the bottom of the Scala type hierarchy.
*
* `Null` is the type of the `null` literal. It is a subtype of every type
* except those of value classes. Value classes are subclasses of [[AnyVal]], which includes
* primitive types such as [[Int]], [[Boolean]], and user-defined value classes.
*
* Since `Null` is not a subtype of value types, `null` is not a member of any such type.
* For instance, it is not possible to assign `null` to a variable of type [[scala.Int]].
*/
sealed trait Null
可见,null只能赋值给引用类型,不能赋值给值类型。而在运行时,Null特征会以抽象类Null$的形式存在于JVM中。
package scala
package runtime
/**
* Dummy class which exist only to satisfy the JVM. It corresponds to
* `scala.Null`. If such type appears in method signatures, it is erased
* to this one. A private constructor ensures that Java code can't create
* subclasses. The only value of type Null$ should be null
*/
sealed abstract class Null$ private ()
这样就阻断了new Null()以及继承Null的可能性,维护了空引用的唯一性。下面举个例子说明Null和null的意义。
scala> var myStr: String = _
myStr: String = null
scala> var myNull: Null = null
myNull: Null = null
scala> myNull = new Null
<console>:12: error: class Null is abstract; cannot be instantiated
myNull = new Null
^
scala> def useNull(n: Null) = { println("Hello null!") }
useNull: (n: Null)Unit
scala> useNull(myStr)
<console>:14: error: type mismatch;
found : String
required: Null
useNull(myStr)
^
scala> useNull(myNull)
Hello null!
Nothing
Nothing在Null的基础上更进一步(说是更退一步也可以)。它是所有Any(包含Null在内)的子类,处于Scala类型体系的最底层,表示“no instance”,即真正意义上的“没有任何实例”。其定义与Null是相同的。
package scala
/** `Nothing` is - together with [[scala.Null]] - at the bottom of Scala's type hierarchy.
*
* `Nothing` is a subtype of every other type (including [[scala.Null]]); there exist
* ''no instances'' of this type. Although type `Nothing` is uninhabited, it is
* nevertheless useful in several ways. For instance, the Scala library defines a value
* [[scala.collection.immutable.Nil]] of type `List[Nothing]`. Because lists are covariant in Scala,
* this makes [[scala.collection.immutable.Nil]] an instance of `List[T]`, for any element of type `T`.
*
* Another usage for Nothing is the return type for methods which never return normally.
* One example is method error in [[scala.sys]], which always throws an exception.
*/
sealed trait Nothing
在运行时,Nothing特征会以抽象类Nothing$的形式存在于JVM中。特别注意,Nothing可以作为异常类型被抛出,后面会见到它的这种用法。
package scala
package runtime
/**
* Dummy class which exist only to satisfy the JVM. It corresponds
* to `scala.Nothing`. If such type appears in method
* signatures, it is erased to this one.
*/
sealed abstract class Nothing$ extends Throwable
由上文的叙述可知,Nothing类型的对象永远无法被赋值,但是它的灵活性也是最大的,可以用来作为任意类型的marker interface。来看下面的例子即可理解。
scala> val myStrList: List[String] = List[Nothing]()
myStrList: List[String] = List()
scala> val myIntList: List[Int] = List[Nothing]()
myIntList: List[Int] = List()
scala> val myStrList2: List[String] = List[Nothing]("123", "456")
<console>:11: error: type mismatch;
found : String("123")
required: Nothing
val myStrList2: List[String] = List[Nothing]("123", "456")
^
<console>:11: error: type mismatch;
found : String("456")
required: Nothing
val myStrList2: List[String] = List[Nothing]("123", "456")
^
Nil
直接来看Nil的定义,它位于scala.collection.immutable包下。
case object Nil extends List[Nothing] {
override def isEmpty = true
override def head: Nothing =
throw new NoSuchElementException("head of empty list")
override def tail: List[Nothing] =
throw new UnsupportedOperationException("tail of empty list")
// Removal of equals method here might lead to an infinite recursion similar to IntMap.equals.
override def equals(that: Any) = that match {
case that1: scala.collection.GenSeq[_] => that1.isEmpty
case _ => false
}
}
可见,Nil就是一个空的List[Nothing],即一个可以封装任何类型元素但又没有元素的容器。由于它确定是空的,所以其head()和tail()方法都应抛出异常,这里就利用了上述Nothing可以作为异常类型的特性。从某种意义上讲,方法抛出异常表示没有按照既定的返回值类型进行返回,所以Nothing的语义正合适。
None
在说None之前,应该先来了解Option。
Scala中的Option与Java 8引入的java.util.Optional语义相同,表示“一个实例有可能不为空,也有可能为空”,是OOP大一统思想下的产物,旨在通过让用户提前判空来避免讨厌的NullPointerException,降低直接返回null的风险。在Scala类Option[+A]的伴生对象中,apply()方法的定义如下。
/** An Option factory which creates Some(x) if the argument is not null,
* and None if it is null.
*
* @param x the value
* @return Some(value) if value != null, None if value == null
*/
def apply[A](x: A): Option[A] = if (x == null) None else Some(x)
当实例x不为null时,返回Some(x),否则返回的就是None了。Some和None都是Optional的子类,定义如下。
@SerialVersionUID(1234815782226070388L) // value computed by serialver for 2.11.2, annotation added in 2.11.4
final case class Some[+A](x: A) extends Option[A] {
def isEmpty = false
def get = x
}
@SerialVersionUID(5066590221178148012L) // value computed by serialver for 2.11.2, annotation added in 2.11.4
case object None extends Option[Nothing] {
def isEmpty = true
def get = throw new NoSuchElementException("None.get")
}
可见,None的本质就是Option[Nothing]。还是举个例子吧。
scala> def getPositive(num: Int): Option[Int] = {
| if (num > 0) Some(num)
| else None
| }
getPositive: (num: Int)Option[Int]
scala> def showPositive(num: Int) = {
| getPositive(num) match {
| case Some(x) => println("Got a positive number")
| case None => println("Got a non-positive number")
| }
| }
showPositive: (num: Int)Unit
scala> showPositive(7)
Got a positive number
scala> showPositive(-7)
Got a non-positive number
Scala可以优雅地使用模式匹配来处理Some和None的情况,所以Option在主要以Scala写成的开源框架(如Spark)中应用甚为广泛。
Unit
Unit在上面的例子中已经出现过多次了,它完全等同于Java里的void,表示函数或方法没有返回值。它的定义如下。
package scala
/** `Unit` is a subtype of [[scala.AnyVal]]. There is only one value of type
* `Unit`, `()`, and it is not represented by any object in the underlying
* runtime system. A method with return type `Unit` is analogous to a Java
* method which is declared `void`.
*/
final abstract class Unit private extends AnyVal {
override def getClass(): Class[Unit] = null
}
可见,Unit是一种特殊的值类型(AnyVal)的子类,并且它不代表任何实际的值类型,故其getClass()方法返回null。在Unit伴生对象的拆装箱逻辑中,我们可以发现:
/** Transform a value type into a boxed reference type.
*
* @param x the Unit to be boxed
* @return a scala.runtime.BoxedUnit offering `x` as its underlying value.
*/
def box(x: Unit): scala.runtime.BoxedUnit = scala.runtime.BoxedUnit.UNIT
在BoxedUnit的定义里,也能够发现Unit等同于void的蛛丝马迹。
public final static Class<Void> TYPE = java.lang.Void.TYPE;
The End
准备迎接双11大潮的洗礼,民那晚安晚安。