整体类层级

Scala 中,所有的类都继承自一个共同的超类,Any。因此所有定义在Any中的方法称为通用方法,任何对象都可以调用。并且在层级的最底层定义了NullNothing,作为所有类的子类。

类层级

NAME

可以看到,顶层类型为Any,下面分为两个大类,AnyVal包含了所有值类AnyRef包含了所有引用类

值类共包含 9 种:Byte, Short, Char, Int, Long, Float, Double, Boolean, 和 Unit。前 8 中与 Java 中的原始类型一致,在运行时都变现为 Java 原始类型。Unit等价于 Java 中的void类型,表示一个方法并没有返回值,而它的值只有一个,写作()

引用类对应于 Java 中的ObjectAnyRef只是java.lang.Object的别名,因此所有 Scala 或 Java 中编写的类都是AnyRef的子类。

Any中定义了一下多个方法:

final def ==(that: Any): Boolean 
final def !=(that: Any): Boolean 
def equals(that: Any): Boolean 
def ##: Int 
def hashCode: Int 
def toString: String

因此所有对象都能够调用这些方法。

原始类的实现方式

Scala 与 Java 以同样的方式存储整数:以 32 位数字存放。这对在 JVM 上的效率以及与 Java 库的互操作性都很重要。标准的操作如加减乘除都被实现为基本操作。

但是,当整数需要被当做 Java 对象看待时,比如在整数上调用toString或将整数赋值给Any类型的变量,这时,Scala 会使用“备份”类java.lang.Integer。需要的时候,Int类型的整数能被透明转换为java.lang.Integer类型的装箱整数

比如一个 Java 程序:

boolean isEqual(int x, int y){
  return x == y;
}
System.out.println(isEqual(1,1))		// true

但是如果将参数类型改为Integer

boolean isEqual(Integer x, Integer y){
  return x == y;
}
System.out.println(isEqual(1,1))		// false

在调用isEqual时,整数 1 会被自动装箱Integer类型,而Integer为引用类型,==在比较引用类型时比较的是引用相等性,因此结果为false

但是在 Scala 中,==被设计为对类型表达透明。对于值类来说,就是自然(数学)的相等。对于引用类型,==被视为继承自Objctequals方法的别名。而这个equals方法最初始被定义为引用相等,但被许多子类重写实现为自然理念上(数据值)的相等。因此,在 Scala 中使用==来判断引用类型的相等仍然是有效的,不会落入 Java 中关于字符串比较的陷阱。

而如果真的需要进行引用相等的比较,可以直接使用AnyRef类的eq方法,它被实现为引用相等并且不能被重写。其反义比较,即引用不相等的比较,可以使用ne方法。

底层类型

类层级的底部有两个类型,scala.Nullscala.Nothing。他们是用统一的方式来处理 Scala 面向对象类型系统的某些边界情况的特殊类型。

Null类是null引用对象的类型,它是每个引用类的子类。Null不兼容值类型,比如把null赋值给值类型的变量。

Nothing类型在 Scala 类层级的最底端,它是任何其他类型的子类型。并且该类型没有任何值,它的一个用处是标明一个不正常的终止,比如scala.sys中的error方法:

def error(message: String): Nothing

调用该方法始终会抛出异常。因为error方法的返回值是Nothing类型,我们可以简便的利用该方法:

def divide(x:Int, y:Int): Int = 
  if (y != 0) x / y
  else error("can't divide by zero!")	// 返回 Nothing,是 Int 的子类型,兼容

另外,空的列表Nil被定义为List[Nothing],因为List[+A]是协变的,这使得Nil可以是任何List[T]实例,T为任意类型。