Chapter08 繼承

1. 擴展類

  • 擴展的方法是使用extends關鍵字
    class Person {
      var name = ""
    }

    class Employee extends Person {
      var salary = 0.0
      def description = "An employee with name " + name + " and salary " + salary
    }

    object Main extends App {
      val fred = new Employee
      fred.name = "Fred"
      fred.salary = 50000
      println(fred.description)
    }
  • 在類、方法或字段前加final關鍵字,這樣類或方法就不會被擴展或重寫。

2. 重寫方法

  • Scala重寫一個非抽象方法,必須用override修飾符。
    class Person {
      var name = ""
      override def toString = getClass.getName + "[name=" + name + "]"
    }

    class Employee extends Person {
      var salary = 0.0
      override def toString = super.toString + "[salary=" + salary + "]"
    }

    object Main extends App {
      val fred = new Employee
      fred.name = "Fred"
      fred.salary = 50000
      println(fred)
    }
  • 調用超類(父類),使用super關鍵字。

3. 類型檢查和轉換

  • isInstanceOf方法,測試某個對象是否屬於某個給定類。

  • asinstancdOf方法,將引用轉換爲子類的引用。

  • classOf測試p是Employee對象但又不是子類。

    class Person {
      var name = ""
      override def toString = getClass.getName + "[name=" + name + "]"
    }

    class Employee extends Person {
      var salary = 0.0
      override def toString = super.toString + "[salary=" + salary + "]"
    }

    class Manager extends Employee

    object Main extends App {
      val r = scala.math.random
      val p = if (r < 0.33) new Person 
        else if (r < 0.67) new Employee 
        else new Manager  
      if (p.isInstanceOf[Employee]) {     
        val s = p.asInstanceOf[Employee] // s has type Employee
        println("It's an employee.")
        s.salary = 50000
        if (p.getClass == classOf[Manager]) {
          println("Actually, it's a manager")
          s.salary *= 2
        }
      }
      println(p)
    }

4. 受保護字段和方法

  • 字段或方法聲明爲protected,這樣的成員可以被任意子類訪問,但不能從其他位置看到。

  • protected的成員對類所屬的包是不可見的。

  • protected[this]將訪問權限限定在當前對象。


5. 超類的構造

  • 只有主構造器可以調用超類的構造器
    class Person(val name: String, val age: Int) {
      override def toString = getClass.getName + "[name=" + name +
        ",age=" + age + "]"
    }

    class Employee(name: String, age: Int, val salary : Double) extends
      Person(name, age) {
      override def toString = super.toString + "[salary=" + salary + "]"
    }

    new Employee("Fred", 42, 50000)

6. 重寫字段

  • 重寫有如下限制:

    1. def只重寫另一個def。
    2. val只能重寫另一個val或不帶參數的def。
    3. var只能重寫另一個抽象的var。

    重寫val/def/var

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

    class SecretAgent(codename: String) extends Person(codename) {
      override val name = "secret" // 不暴露真名
      override val toString = "secret" // ...或類名
    }

    val fred = new Person("Fred")
    fred.name
    val james = new SecretAgent("007")
    james.name

    // 用val重寫抽象類的def
    abstract class Person {  
      def id: Int    
    }

    class Student(override val id: Int) extends Person

    class SecretAgent extends Person {
      override val id = scala.util.Random.nextInt
    }

    val fred = new Student(1729)
    fred.id
    val james = new SecretAgent
    james.id

7. 匿名子類

  • 通過包含帶有定義或重寫的代碼塊的方法創建一個匿名的子類。

  • 匿名類作爲參數的定義類型爲:Person{def greeting:String}

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

    val alien = new Person("Fred") {
      def greeting = "Greetings, Earthling! My name is Fred."
    }

    def meet(p: Person{def greeting: String}) {
      println(p.name + " says: " + p.greeting)
    }

    meet(alien)

8. 抽象類

  • abstract關鍵字標記不能被實例化的類,因爲它的某個或幾個方法沒有被定義,這種沒有方法體的方法是抽象方法

  • 某個類存在抽象方法,則必須申明爲abstract。有抽象方法以及下面將提到的抽象字段的存在才導致了抽象類的出現。

  • 子類中重寫超類的抽象方法時,不需要使用override關鍵字。

    abstract class Person(val name: String) {
      def id: Int // 沒有方法體的方法是 抽象方法 
    }

    class Employee(name: String) extends Person(name) {
      def id = name.hashCode // override 不需要
    }

    val fred = new Employee("Fred")
    fred.id

9. 抽象字段

  • 抽象字段:沒有初始值的字段。
    abstract class Person {
      val id: Int      // 沒有初始化,帶有抽象getter方法的抽象字段
      var name: String // 帶有抽象getter和setter方法
      override def toString = getClass.getName + "[id=" + id + ",name=" + name + "]"
    }

    class Employee(val id: Int) extends Person { // 子類具體的id
      var name = "" 
    }

    val james = new Employee(7)

    val fred = new Person {
      val id = 1729
      var name = "Fred"
    }

10. 構造順序和提前定義

  • 問題來源:子類將父類方法重寫後,父類構造時對應的方法失效,由子類來構造。如下例,實際構造完成後rannge的值爲2。
    class Creature {
      val range: Int = 10
      val env: Array[Int] = new Array[Int](range)
    }

    class Ant extends Creature {
      override val range = 2
    }
  • 有三種方法解決上述問題

    1. 將val申明爲final,安全但不靈活。

    2. 將val申明爲lazy,安全但不高效。

    3. 子類中使用提前定義

  • 提前定義:在超類構造之前初始化子類val字段,將val字段放在extends關鍵字之後的一個塊中,塊中不能包含類中其他字段。並且超類前用with關鍵字。

    class Creature {
      val range: Int = 10
      val env: Array[Int] = new Array[Int](range)
    }

    class Ant extends Creature {
      override val range = 2
    }

    class Bug extends {
      override val range = 3
    } with Creature

11. scala繼承層級

  • 所有的Scala類都實現ScalaObject這個接口。

  • Any類是整個繼承層級的根節點。

  • Null唯一實例是null值,可賦值給任何引用,但不能賦值給值類型變量

  • Unit類型它的值是()可賦值給任何值變量

  • Nothing類型沒有實例。

     scala繼承層級


【待續】

發佈了84 篇原創文章 · 獲贊 324 · 訪問量 66萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章