前言
本节将介绍Scala中的继承关系,包括Any、AnyVal、AnyRef。
环境:
Windows + Scala-2.12.8
1. Scala 的类继承关系
在Any 类的源代码中有如下方法:
package scala
abstract class Any {
def equals(that: Any): Boolean
def hashCode(): Int
def toString(): String
final def getClass(): Class[_] = sys.error("getClass")
final def ==(that: Any): Boolean = this equals that
final def != (that: Any): Boolean = !(this == that)
final def ##(): Int = sys.error("##")
final def isInstanceOf[T0]: Boolean = sys.error("isInstanceOf")
final def asInstanceOf[T0]: T0 = sys.error("asInstanceOf")
}
Scala 提供了九个内建的值类:Byte、Short、Char、Int、Long、Float、Double、Boolean和Unit。
如果你进行这样的尝试是错误的:
在Int 类型的源代码中没有如下的方法,但是却可以使用:
因为,max、abs都定义在scala.runtime.RichInt类中,并且存在从Int 类到 RichInt 的隐式转换。所以,Int 类型的对象能够使用这些方法。
2. 基本类型的实现机制
在Scala 中类型为Int 的整数在必要时都会透明地被转换成类型为java.lang.Integer 的“装箱整数”。
不过,Scala中的装箱跟Java 比要透明的多。
比如,在Java 中:
public class Test {
public static void main(String[] args) {
System.out.println(isEqual(13, 13));
System.out.println(isEqual2(13, 13));
}
static boolean isEqual(int x, int y) {
return x == y;
}
static boolean isEqual2(Integer x, Integer y) {
return x == y;
}
}
true
true
但是,由于Java 的整形缓存机制(-128~127)会缓存和重用,也就是说isEqual2(13, 13) 的结果应该是false,由于缓存机制所以是true,比如换成大于127的数:(仅仅是博主在测试中遇到的一个问题)
在Scala 中:
在Scala中,== 和 eq 的对比如下:
3. 底型类
在Scala 中,最底部为scala.Null 和 scala.Nothing。它们是Scala面向对象的类型系统用于统一处理某些”极端情况”的特殊类型。
Null 并不兼容与值类型(上面的类继承图也可以看出来),如:
Nothing的用途之一是给出非正常终止的信号。
如Predef 标准库中有一个error 方法,定义如下:
def error(message: String): Nothing =
throw new RuntimeException(message)
error 的返回类型是Nothing,这告诉使用方 该方法并不会正常返回(它会抛出异常)。由于Nothing 是每个其他类型的子类型,可以非常灵活的使用error 这样的方法,如:
注:这是书上的例子(但是经博主查看源码,已经没有了error 方法,所以不能运行,猜测可能是版本问题)
def divide(x: Int, y: Int): Int = {
if (y != 0) x / y
else error("can't divide by zero")
}
虽然不能运行,还是解析一下。因为error 的返回类型为Nothing,而divide 的返回类型是Int。如果,Nothing 不是Int 的子类型,编译时不能通过的。但是,Nothing 是Int 的子类型,所以编译是能够通过的(error存在的前提下)。
4. 定义自己的值类型
只有特定的几个类可以成为值类型。它必须有且仅有一个参数,并且在内部除了def 之外不能有其他任何东西。不经如此,也不能有其他类扩展自值类,如:
class Dollars(val amount: Int) extends AnyVal {
override def toString() = "$" + amount
}
(amount 为参数化字段)是不是觉得有点像Scala 内建的值类型:
另外,还因避免类型单一化。要想尽可能发挥Scala 类继承关系的好处,应该试着对每个领域定义一个新类。比如,想要生成一段HTML 代码,你可能会这样做:
def title(text: String, anchor: String, style: String): String =
s"<a id='$anchor'><h1 class='$style'>$text</h1></a>"
你传参顺序错误的情况下,编译器不能帮你检测出来,造成了如下结果:
所以我们需要对每一个领域定义一个细微类型(既没有方法,也没有字段),如:
class Anchor(val value: String) extends AnyVal
class Style(val value: String) extends AnyVal
class Text(val value: String) extends AnyVal
class Html(val value: String) extends AnyVal
然后,可以定义一个更丰满的title方法:
def title(text: Text, anchor: Anchor, style: Style): Html = {
new Html(
s"<a id='${anchor.value}'>" +
s"<h1 class='${style.value}'>" +
text.value +
"</h1></a>"
)
}
现在,如果传参类型不正确,编译器会报错,如:
只有正确的传参才可以: