控制復(fù)雜性是計(jì)算機(jī)編程的本質(zhì)。-- Brian Kernigan
Scala
繼承體系結(jié)構(gòu)設(shè)計(jì)非常巧妙,它沒有特殊地對待「基本數(shù)據(jù)類型」,將萬物視為對象鉴吹。此外,Scala
在頂層引入Any
惩琉,它是所有類的父類豆励;而在底層引入了Nothing
,它是所有類的子類瞒渠,整個(gè)系統(tǒng)的設(shè)計(jì)保持一致和完整良蒸。
Any
總體上,Scala
的對象可分為兩個(gè)類型:
- 引用類型(Reference Types):繼承自
AnyRef
- 值類型(Value Types):繼承自
AnyVal
而Any
則是AnyVal, AnyRef
的父類伍玖。也就是說嫩痰,Any
是所有Scala
類型的父類,它內(nèi)置于Scala
內(nèi)部私沮,由編譯器實(shí)現(xiàn)始赎。
元類
Class<T>
的實(shí)例代表了T
類型的元數(shù)據(jù);對于每個(gè)T
類型仔燕,在JVM
運(yùn)行時(shí)有且僅有一個(gè)Class<T>
的實(shí)例存在。
在Scala
里魔招,要獲取T
類型的Class<T>
實(shí)例晰搀,可以使用classOf
的實(shí)用方法。
classOf[String]
其中办斑,classOf
定義在Predef
之中外恕,由Scala
的編譯器實(shí)現(xiàn)。
def classOf[T]: Class[T] = ???
強(qiáng)制轉(zhuǎn)換
Scala
并沒有提供強(qiáng)制類型轉(zhuǎn)換的特殊語法乡翅,它們是通過調(diào)用isInstanceOf/asInstanceOf
方法實(shí)現(xiàn)鳞疲。
if (obj.isInstanceOf[Point])
val other = obj.asInstanceOf[Point]
它們定義于Any
之中,它們都具有一個(gè)類型參數(shù)蠕蚜。
class Any {
final def isInstanceOf[T]: Boolean = ???
final def asInstanceOf[T]: T = ???
}
相等性
Scala
使用==/!=
比較對象間的邏輯相等性尚洽,而使用eq/ne
比較對象間的物理相等性。其中靶累,Any
中的==/!=
方法使用equals
實(shí)現(xiàn)腺毫,并處理了null
值比較的情況癣疟。而Any
中定義的equals
默認(rèn)使用eq
比較對象間的物理相等性。也就是說潮酒,如果一個(gè)類未重寫equals
方法睛挚,==/!=
方法比較對象間的物理相等性。
class Any {
final def !=(that: Any): Boolean = !(this == that)
final def ==(that: Any): Boolean =
if (null eq this) null eq that
else this equals that
def equals(that: Any): Boolean = this eq that
}
AnyRef
AnyRef
是所有「引用類型」的根類急黎,它等價(jià)于Object
扎狱。引用類型要么引用new
構(gòu)造的實(shí)例,要么引用null
值勃教。
val s: String = null
對象一致性
AnyRef
中定義了eq/ne
委乌,用于比較對象間的物理相等性。其中荣回,
- 對于非
null
的x: AnyRef
,x eq null
與null eq x
都返回false
遭贸; - 但是,對于
null eq null
則返回true
心软。
class AnyRef {
final def ne(that: AnyRef): Boolean = !(this eq that)
final def eq(that: AnyRef): Boolean =
(this, that) match {
case (null, null) => true
case (null, _) => false
case (_, null) => false
case _ => this same that
}
// same為編譯器內(nèi)部實(shí)現(xiàn)的壕吹,比較兩個(gè)引用類型的物理相等性
private def same(that: AnyRef): Boolean = ???
}
Null
Null
類型為所有引用類型的子類,其擁有唯一的實(shí)例:null
删铃。
package scala
abstract final class Null private extends AnyRef
AnyVal
AnyVal
是所有「值類型」的根類耳贬,包括Unit, Bolean, Char, Byte, Short, Int, Long, Float, Double
。
值類型的實(shí)例猎唁,由編譯器將其映射為原生的基本數(shù)據(jù)類型咒劲,存取效率相當(dāng)高效。但是诫隅,值類型不能使用new
構(gòu)造實(shí)例腐魂,也不能持有null
值,而應(yīng)該使用「字面值」直接初始化逐纬。
val MAX_NUM: Int = null // Error
Unit
Unit
類型是一個(gè)特殊的值類型蛔屹,它等價(jià)于Java
中的void
。它擁有唯一的實(shí)例:()
豁生,即0
個(gè)元素的元組兔毒。
classOf[Unit] // Class[Unit] = void
().getClass // Class[Unit] = void
classTag[Unit] // scala.reflect.ClassTag[Unit] = Unit
classTag[Unit].runtimeClass // Class[_] = void
過程
返回值類型為Unit
的函數(shù)常常稱為「過程」。例如甸箱,Runnable
中的run
方法就是一個(gè)典型的過程育叁。
trait Runnable {
def run(): Unit
}
按照慣例,run
有可能產(chǎn)生副作用芍殖,為此run
顯式地聲明了()
豪嗽。
區(qū)分Int, RichInt, Integer
探秘Int
Scala
是一門純的面向?qū)ο蟮某绦蛟O(shè)計(jì)語言,它沒有特殊地對待原生的基本數(shù)據(jù)類型,例如int, short, long, char
等昵骤。
1 + 2
它實(shí)際上是一個(gè)函數(shù)調(diào)用過程树碱,等價(jià)于
1.+(2)
事實(shí)上,+
方法定義在Int
類中变秦。
final abstract class Int private extends AnyVal {
def +(x: Int): Int = ???
...
}
為了提升效率成榜,Int
將映射為JVM
中的int
。
探秘RichInt
求取1
至10
的和蹦玫,可以如此實(shí)現(xiàn)赎婚。
(1 to 10).sum
它等價(jià)于:
1.to(10).sum
但是,Int
中并沒有定義to
方法樱溉,但1 to 10
為什么能夠工作呢挣输?事實(shí)上,在Predef
中定義了Int
到RichInt
的隱式轉(zhuǎn)換福贞。
object Predef {
implicit def intWrapper(x: Int) = new scala.runtime.RichInt(x)
...
}
而RichInt
中剛好定義了一個(gè)to
方法撩嚼,它創(chuàng)建了一個(gè)Range.Inclusive
類型的實(shí)例。
package scala.runtime
class RichInt {
def to(end: Int): Range.Inclusive = Range.inclusive(self, end)
...
}
RichInt
是一個(gè)Int
的富包裝類型挖帘。這樣的設(shè)計(jì)機(jī)制完丽,不僅保持了Int
的高效,而且也保證了RichInt
良好的可擴(kuò)展性拇舀。
探秘Integer
Integer
是Int
的包裝器逻族,它的實(shí)例分配于堆中。對于Scala
骄崩,自動(dòng)裝箱和自動(dòng)拆箱是通過隱式轉(zhuǎn)換完成的聘鳞。或者說要拂,自動(dòng)裝箱和自動(dòng)拆箱僅僅是隱式轉(zhuǎn)換的一個(gè)應(yīng)用場景而已抠璃。
object Predef {
...
implicit def int2Integer(x: Int) = Integer.valueOf(x)
implicit def Integer2int(x: Integer): Int = x.intValue
}
仿真Boolean
為了加深理解值類型的工作原理,這里自制仿真實(shí)現(xiàn)了一個(gè)Boolean
宇弛,其行為等價(jià)于標(biāo)準(zhǔn)庫的Boolean
實(shí)現(xiàn)鸡典;但是,此處的Boolean
實(shí)現(xiàn)采用了函數(shù)式的設(shè)計(jì)思維枪芒。
函數(shù)式結(jié)構(gòu)
Boolean
是一個(gè)典型的函數(shù)式的數(shù)據(jù)結(jié)構(gòu),true
和false
是Boolean
的兩個(gè)字面值谁尸。其中舅踪,eval
相當(dāng)于if-else
表達(dá)式。
sealed trait Boolean {
def eval[T](t: => T, e: => T): T
}
object true extends Boolean {
def eval[T](t: => T, e: => T): T = t
}
object false extends Boolean {
def eval[T](t: => T, e: => T): T = e
}
短路求值
&&
與&
之間的差異在于前者擁有「短路求值」的特性良蛮,而后者沒有抽碌;
sealed trait Boolean {
def eval[T](t: => T, e: => T): T
def &&(x: => Boolean): Boolean = eval(x, false)
def ||(x: => Boolean): Boolean = eval(true, x)
def unary_! : Boolean = eval(false, true)
def &(x: Boolean): Boolean = eval(x, false)
def |(x: Boolean): Boolean = eval(true, x)
def ^(x: Boolean): Boolean = eval(false, True)
}
相等性
對于兩個(gè)Boolean
實(shí)例,也可以通過eval
實(shí)現(xiàn)==/!=
的比較邏輯。
sealed trait Boolean {
...
def eval[T](t: => T, e: => T): T
def ==(x: Boolean): Boolean = eval(x, !x)
def !=(x: Boolean): Boolean = eval(!x, x)
}
Nothing
Nothing
是一個(gè)特殊的類型货徙,它處在繼承層次的最底部左权,它是所有類的子類。雖然Nothing
沒有任何實(shí)例存在痴颊,但它在Scala
的類型系統(tǒng)中扮演了重要的角色赏迟。
package scala
final abstract class Nothing extends Throwable
空類型
Nil
繼承自List[Nothing]
,且為唯一的單鍵對象蠢棱;因?yàn)?code>List[+A]是協(xié)變的锌杀,所有對于任意的類型A
,List[Nothing]
都是List[A]
的子類泻仙。
sealed abstract class List[+A]
final case class ::[A](head: A, tail: List[A]) extends List[A]
final case object Nil extends List[Nothing]
表示異常
Nothing
繼承自Throwable
糕再,它表示程序的異常終止。例如玉转,Predef
中定義的???
占位方法突想,其返回值類型就是聲明為Nothing
。
object Predef {
def ??? : Nothing = throw new NotImplementedError
}
在實(shí)施
TDD
實(shí)踐過程中究抓,為了快速編譯通過猾担,可以使用???
表示占位實(shí)現(xiàn)。