Scala繼承體系結(jié)構(gòu)

控制復(fù)雜性是計(jì)算機(jī)編程的本質(zhì)。-- Brian Kernigan

Scala繼承體系結(jié)構(gòu)設(shè)計(jì)非常巧妙,它沒有特殊地對(duì)待「基本數(shù)據(jù)類型」,將萬(wàn)物視為對(duì)象。此外,Scala在頂層引入Any,它是所有類的父類;而在底層引入了Nothing,它是所有類的子類,整個(gè)系統(tǒng)的設(shè)計(jì)保持一致和完整。

Scala繼承體系結(jié)構(gòu)

Any

總體上,Scala的對(duì)象可分為兩個(gè)類型:

  • 引用類型(Reference Types):繼承自AnyRef
  • 值類型(Value Types):繼承自AnyVal

Any則是AnyVal, AnyRef的父類。也就是說(shuō),Any是所有Scala類型的父類,它內(nèi)置于Scala內(nèi)部,由編譯器實(shí)現(xiàn)。

元類

Class<T>的實(shí)例代表了T類型的元數(shù)據(jù);對(duì)于每個(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)換的特殊語(yǔ)法,它們是通過(guò)調(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使用==/!=比較對(duì)象間的邏輯相等性,而使用eq/ne比較對(duì)象間的物理相等性。其中,Any中的==/!=方法使用equals實(shí)現(xiàn),并處理了null值比較的情況。而Any中定義的equals默認(rèn)使用eq比較對(duì)象間的物理相等性。也就是說(shuō),如果一個(gè)類未重寫equals方法,==/!=方法比較對(duì)象間的物理相等性。

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

對(duì)象一致性

AnyRef中定義了eq/ne,用于比較對(duì)象間的物理相等性。其中,

  • 對(duì)于非nullx: AnyRef, x eq nullnull eq x都返回false
  • 但是,對(duì)于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
過(guò)程

返回值類型為Unit的函數(shù)常常稱為「過(guò)程」。例如,Runnable中的run方法就是一個(gè)典型的過(guò)程。

trait Runnable {
  def run(): Unit
}

按照慣例,run有可能產(chǎn)生副作用,為此run顯式地聲明了()

區(qū)分Int, RichInt, Integer

探秘Int

Scala是一門純的面向?qū)ο蟮某绦蛟O(shè)計(jì)語(yǔ)言,它沒有特殊地對(duì)待原生的基本數(shù)據(jù)類型,例如int, short, long, char等。

1 + 2

它實(shí)際上是一個(gè)函數(shù)調(diào)用過(guò)程,等價(jià)于

1.+(2)

事實(shí)上,+方法定義在Int類中。

final abstract class Int private extends AnyVal {
  def +(x: Int): Int = ???
  ...
}

為了提升效率,Int將映射為JVM中的int。

探秘RichInt

求取110的和,可以如此實(shí)現(xiàn)。

(1 to 10).sum

它等價(jià)于:

1.to(10).sum

但是,Int中并沒有定義to方法,但1 to 10為什么能夠工作呢?事實(shí)上,在Predef中定義了IntRichInt的隱式轉(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

IntegerInt的包裝器,它的實(shí)例分配于堆中。對(duì)于Scala,自動(dòng)裝箱和自動(dòng)拆箱是通過(guò)隱式轉(zhuǎn)換完成的?;蛘哒f(shuō),自動(dòng)裝箱和自動(dòng)拆箱僅僅是隱式轉(zhuǎn)換的一個(gè)應(yīng)用場(chǎ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)庫(kù)的Boolean實(shí)現(xiàn);但是,此處的Boolean實(shí)現(xiàn)采用了函數(shù)式的設(shè)計(jì)思維。

函數(shù)式結(jié)構(gòu)

Boolean是一個(gè)典型的函數(shù)式的數(shù)據(jù)結(jié)構(gòu),truefalseBoolean的兩個(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)
}
相等性

對(duì)于兩個(gè)Boolean實(shí)例,也可以通過(guò)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],且為唯一的單鍵對(duì)象;因?yàn)?code>List[+A]是協(xié)變的,所有對(duì)于任意的類型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í)踐過(guò)程中,為了快速編譯通過(guò),可以使用???表示占位實(shí)現(xiàn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容