Trait詳解


基礎知識

  1. Trait(特征):是一種特殊的概念,相當於 Java 的接口,實際上它比接口還功能強大,它還可以定義屬性和方法的實現

  2. Trait的定義:定義的方式和定義類的方式相同
  3. scala不支持對類進行多繼承,但是支持多重繼承trait,使用with關鍵字即可,解決類的單繼承問題

  4. 類繼承trait后,必須實現其中的抽象方法和抽象字段

  5. trait也可以繼承自class,此時這個class就會成為所有繼承該trait的類的父類

抽象方法和抽象字段

  1. 在triat中可以定義抽象方法,定義的抽象方法可以直接在trait中引用
  2. Triat可以定義抽象field,定義的抽象字段可以在trait中直接引用,此時抽象字段有默認值
object test11 {
  trait Sayhello{
    val name :String  //抽象字段
    println(name)  //調用抽象字段
    def sayhello(name:String)=println(s"hello,$name")  //具體方法調用抽象字段
  }
  trait MakeFriends{
    def makeFrinds(str:String)  //抽象方法
    def log(msg: String) {} //{}表示不是抽象方法
  }
  class Person(val name:String) extends Sayhello with MakeFriends{  //with實現多繼承
    def makeFrinds(str:String)=print(s"可以交個朋友嗎?$str")   //實現MakeFriends中的抽象方法
  }
  def main(args: Array[String]): Unit = {
    val person=new Person("Wei")
    person.sayhello("Tom")
    person.makeFrinds("Tom")
  }
}

 

具體方法和具體字段

  1. 就像普通類中的具體方法和具體字段一樣
object test12{
      trait Person {
        val age: Int = 2
        def eat(food: String) = println(s"吃 $food")
      }
      class Student(val name: String) extends Person {}

  def main(args: Array[String]): Unit = {
    val person = new Student("tom")
    println(person.age)
    person.eat("橘子")
  }
}

 

高級知識

實例對象繼承trait

  1. 創建對象的時候只需要某個實例對象繼承trait,而不需要類中的其他對象繼承trait
  2. 對象動態繼承的trait會覆蓋對象實現類的成員(Man覆蓋了Person方法)
object  test21{
  trait Person {
    def eat(food: String) {println("不喜歡吃"+food)}
  }
  trait Man extends Person {
    override def eat(food: String){println("喜歡吃"+food)}
  }

  class Teacher(val name: String) extends Person {
    def sayHello {
      println("你好,我是" + name)
      eat("水果") }
  }
  def main(args: Array[String]): Unit = {
    val p1 = new Teacher("wiki")
    p1.sayHello
    println("\t")
    val p2 = new Teacher("jack") with Man
    p2.sayHello
  }
}

 

調用鏈

  1. 類繼承traitA,traitA繼承traitB,traitB繼承traitC,traitC繼承traitD,如果要同時調用trait[A-D]中相同的方法fun1,只要將只需要將super.fun1放入到fun1中即可,形成一個調用鏈條
object  test22 {
  trait KeyWord {
    def logger(data: String): Unit = {
      println("第一次密碼是:" + data)
    }
  }
  trait KeyWord1 extends KeyWord {
    override def logger(data: String): Unit = {
      println("第二次密碼是:" + data)
      super.logger(data)
    }
  }
  trait KeyWord2 extends KeyWord1 {
    override def logger(data: String): Unit = {
      println("第三次密碼是:" + data)
      super.logger(data)
    }
  }
  class KeyWord3 extends KeyWord2 {
    def log(data: String): Unit = {
      logger(data)
    }
  }
  def main(args: Array[String]): Unit = {
    val s = new KeyWord3
    s.log("car360")
  }
}
/**
  * 輸出結果:
  * 第三次密碼是:car360
  * 第二次密碼是:car360
  * 第一次密碼是:car360
  */

 

abstract override

  1. trait可以直接引用抽象方法,但是子類中不可以直接引用父類的抽象方法,需要引用必須用abstract override修飾
object  test23{
  trait Person {
    def play(str: String)
    def eat(str: String) { play(str) }
  }
  trait Teacher extends Person {
    abstract override def play(str: String) { super.play(str) }
    def eat(str: String) { super.eat(str) }   //會報錯
        def say(str: String) { super.play(str) }
  }
}

 

構造機制

  1. trait也有構造函數
  2. 繼承了trait的類的構造機制如下:
    1. 父類的構造函數先執行
    2. 類繼承的traitA如果有父traitB,父traitB的構造代碼先執行(多個trait繼承同一個父trait,則父trait只會構造一次)
    3. trait的構造代碼執行,多個trait從左到右依次執行
    4. 子類的構造函數執行
object  test24{
  class Person { println("Person's constructor!") }
  trait Number { println("Number's constructor!") }
  trait First extends Number { println("First's constructor!") }
  trait Second extends Number { println("Second's constructor!") }
  class Student extends Person with First with Second {
    println("Student's constructor!")
  }
  def main(args: Array[String]): Unit = {
    val stu = new Student
  }
}
//運行結果:
//Person's constructor!
//Number's constructor!
//First's constructor!
//Second's constructor!
//Student's constructor!

 

 

字段的初始化

 

  1.  trait沒有接收參數的構造函數
  2. trait中的抽象字段都有默認值
  3. 提前定義:在調用trait的構造函數時候重寫了字段值
  4. lazy:不能用於抽象字段,只能用於具體字段

方法一:提前定義

object test25 {
  trait Person{
    val num:String
    println(num)  
    println(num.toInt+123)
  }
  class Student(number: String) extends Person{
    val num:String=number
    print("the new num is "+num)
  }
  def main(args: Array[String]): Unit = {
//    val s= new Person("123") //會報錯因為他會先執行trait的println num此時是null,這時就需要用到提前定義了
    val p = new {
      override val num: String = "123"
    } with Student("456")           //注意:傳入的參數是123而不是456
  }
}

 

方法二:用lazy

trait Person{
  lazy  val num:String=null //注意lazy不能用於抽象字段,只能用與具體字段
  println(num.toInt+123)
}
class Student (number: String) extends Person{
  override lazy val num:String=number
  print("the new num is "+num)
}
object test {
  def main(args: Array[String]): Unit = {
    val p = new Student("123")
  }
}

 

 




免責聲明!

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



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