Scala 繼承


1. 繼承

Scala 通過 extends 關鍵字來繼承類.

那么繼承一個類有什么好處呢?

  1. 子類擁有繼承自超類的方法和字段(即為val(常量), var(變量)所定義的)
  2. 可以添加自己需要的新方法和新字段,
  3. 不但可以重寫超類的方法, 還可以重寫超類的字段.

final 關鍵字

Scala 中, 不僅可以將類聲明為 final, 而且可以將字段和方法聲明為 final

當類被聲明為 final 時, 類不可以被繼承; 當方法和字段被聲明為 final 時, 對應的方法和字段不可以被子類重寫, 看看下面這個例子就一目了然

class Person {
  final val name = "person"
  val age = 1
}

class Kid extends Person {
  // 報錯: Value 'name' can not override final member
  // override val name = "jerry" 
  override val age = 2 
}

object test extends App {
  val k = new Kid;
  println(k.age)  // 2
}

2. 重寫方法

在 Scala 中重寫一個 非抽象方法 必須使用 override 修飾符, 如:

 override def toString: String = getClass.getName + "[name=" + name + "]"

override修飾符可以在多個常見的情況下給出有用的錯誤提示, 包括:

  1. 當你拼錯了要重寫的方法名和字段名
  2. 當你不小心在新方法中使用了錯誤的參數類型
  3. 當你在超類中引入了新方法, 而這個新方法和子類的方法抵觸

Scala 使用 super 關鍵字調用超類的方法, super.toString 相當於 Person.toString

class Person {

  val name = "person"
  val age = 1
  
  override def toString: String = getClass.getName + "[name=" + name + "]"

}

class Kid extends Person {

  override val name = "kid"
  override val age = 2

  override def toString: String = super.toString + "[age=" + age + "]"
}

object test extends App {
  val person = new Kid;
  println(person.age)  // 2
  println(person)  // chap08.Kid[name=kid][age=2]
}

3. 類型檢查和轉換

Scala語言中可以使用 isInstanceOf[T] 方法, 測試某個對象實際類型是否屬於某個給定類T或者類T的子類; 測試成功之后可以用 asInstanceOf[T] 方法將對象引用轉化為的(子類)類T引用(一般來說對象的引用類型是T的父類, 而實際類型是T或者T的子類)

  if (person.isInstanceOf[Kid]) {
    val k = person.asInstanceOf[Kid]
  }

如果 person 是null, 則 person.isInstanceOf[Kid] 返回false, kid.asInstanceOf[Kid] 返回null;
如果 person 不是一個 Kid, kid.asInstanceOf[Kid]將拋出異常

如果要測試 person 指向Kid類又不是其子類, 使用如下方法:

if (person.getClass == classOf[Kid]) {
    val s = person.asInstanceOf[Kid]
  }

4. 超類構造

類有一個主構造器和任意數量的輔助構造器, 而每個輔助構造器都必須以對先前定義的輔助構造器或者主構造器的調用開始, 這樣做的結果就是:

輔助構造器永遠都不可能直接調用超類的構造器; 子類的輔助構造器最終都會調用主構造器; 只有主構造器而已調用超類的構造器.

主構造器是和類的定義交織在一起, 調用超類的構造器同樣也交織在一起

class Kid(gender: String, val height: Double) extends Person(gender)

Kid類有2個參數, 一個被"傳遞"到超類

scala語言的(主)構造器中, 你不能調用super(paras)

5. 重寫字段

Scala 的字段(Fields)是指 一個 var或者val 定義的變量(或值)和對應的getter/setter 方法

你可以用一個同名的val字段重寫一個val或者不帶參數的def, 子類有一個私有字段和一個共有的getter方法, 而這個getter方法重寫了超類的getter方法.

class Smiler(val happy: String) {
  override def toString: String = getClass.getName + "[happy: " + happy + "]"
}

class Laughter(veryhappy: String) extends Smiler(veryhappy) {
  override val happy: String = "Laughter"
  override val toString: String = super.toString
}

更常見的例子是 val 重寫抽象的 def,就像這樣:

abstract class Smiler(val happy: String) {
  def degree: Int
}

class Laughter(h: String, override val degree: Int) extends Smiler(h) {
}

注意如下限制:

  • def 只能重寫另一個def
  • val 只能重寫另一個val或者不帶參數的def
  • var 只能重寫另一個抽象的var

6. 匿名子類

你可以通過包含帶有定義或重寫的代碼塊的方式創建一個匿名子類,比如

val alien = new Person("good") {
    def greeting = "hi, good"
    }

7. 抽象類與抽象字段

  • 不需要對抽象方法和抽象字段用 abstract 關鍵字
  • 子類中重寫超類的抽象方法和抽象字段時, 可以不寫 override 關鍵字, 不過 scala in depth 一書分析了總是使用 override 關鍵字會帶來更多的好處, 那就加上吧
  • 只要類中存在抽象方法, 該類必須聲明為 abstract

7.1 抽象類

Scala 中使用 abstract 關鍵字來標記不能實例化的類, 通常是因為它的某個或者幾個方法沒有完整定義. 例如

abstract class Smiler(val happy: String) {
  def degree: Int
}
  • 在Scala中, 不需要對抽象方法用 abstract 關鍵字, 只是省去其方法體
  • 只要類中存在抽象方法, 該類必須聲明為 abstract
  • 子類重寫超類的抽象方法時, 不需要 override 關鍵字
class Laughter(h: String) extends Smiler(h) {
  def degree = h.hashCode 
}

7.2 抽象字段

除了抽象方法外, 類還可以有抽象字段; 抽象字段就是一個沒有初始值的字段. 具體的子類必須提供具體的字段; 和方法一樣, 子類中重寫超類的抽象字段時, 不需要override關鍵字

abstract class Abstract {
  val id: Int // 沒有初始化, 這是一個帶有getter方法的抽象字段
  var name: String // 沒有初始化, 這是一個帶有getter和setting方法的抽象字段

}

class AbstractField(val id: Int) extends  Abstract {

  var name = getClass.getName  // override 可選
}

可以隨時用匿名類型來定制抽象字段

val laught = new Abstract {
  val id = 10
  var name = "laught"
  }

8. Scala繼承層級

  1. 與Java基本類型相對應的類以及Unit類型(相當於Java的void)都擴展自AnyVal
  2. 所有其他類都是AnyRef的子類, Any是整個繼承層級的根節點, AnyVal和AnyRef擴展自Any類
  3. Any類定義了isInstanceOf asInstanceOf方法, 以及用於相等性判斷和哈希碼方法, AnyVal並沒有追加方法, 只是所有值類型的一個標記
  4. Null類型唯一的實例就是null, 你可以將null賦值給任何引用, 但不能賦值給值類型的變量, 舉例來說不能講Int賦值為null
  5. Nothing類型沒有實例, 它對於泛型結構很有用, 比如說空列表Nil是List[Nothing], 它是List[T]的子類型, T可以是任何類型

Scala中的Nothing類型和Java中void不是一個概念;
Scala中void由Unit類型表示, 該類型只有一個值, 那就是();
<<快學Scala>> 中所說雖然Unit不是任何類型的超類, 但編譯器允許任何值來替換成(), 不過現在測試scala2.12.2, Scala已經修改這個語法, 現在不管傳入什么值, printUnit的輸出結果都是()

def printAny(x: Any) {print(x)}
def printUnit(x: Unit) {print(x)}
printAny("happy")
printUnit("happy")

/*output:
happy
()
/*

9. 對象相等性

Scala中調用 ==, 如果比較的是引用類型, Scala會先做null檢查, 然后調用equals 方法



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM