1. 程式人生 > >Scala09——Scala面向物件程式設計之特質trait

Scala09——Scala面向物件程式設計之特質trait

1、將trait作為介面使用

在 Scala中Trait 為重用程式碼的一個基本單位。 將traits作為Java中的介面使用,一個 Traits 封裝了方法和變數(但和 Interface 相比,它的方法可以有實現,這一點有點和抽象類定義類似)。 但和類繼承不同的是,Scala 中類繼承為單一繼承,子類只能有一個父類。但一個類可以和多個 Trait 混合,使用 with 關鍵字即可。這些 Trait 定義的成員變數和方法也就變成了該類的成員變數和方法。 類可以使用extends關鍵字繼承trait,(這裡不是 implement,而是extends) ,在Scala中沒有 implement 的概念,無論繼承類還是trait,統一都是 extends;類繼承後,必須實現其中的抽象方法,實現時,不需要使用 override 關鍵字; 由此可以看出 Trait 集合了 Interface 和抽象類的優點,同時又沒有破壞單一繼承的原則。

package com.fgm.traits

trait SpeakTrait {
  def speak:Unit
}

trait EatingTrait{
  val eating="food is meat"
}

abstract class Animal{
  val age=10
  def running:Unit
}
//可以單繼承類Animal,可以和多個trait混合
class Dog(val name:String ) extends Animal with SpeakTrait with EatingTrait{
  override def speak: Unit = {
    println("wangwangwang")
  }

  def running: Unit = {
    println("He is running ")
  }
}
object Dog{
  def main(args: Array[String]): Unit = {
    val dog = new Dog("一條狗")
    println(dog.name)
    dog.speak
    dog.running
    println(dog.age)
    println(dog.eating)
  }
}

2、trait中定義具體的方法和field

Scala中的trait不僅可以定義抽象方法,還可以定義具體的方法,此時 trait 更像是包含了通用方法的工具,可以認為trait還包含了類的功能。 trait 可以定義具體的 field,此時繼承 trait 的子類就自動獲得了 trait 中定義的 field; 從trait中獲取field的方式與繼承class獲得field的方式不同: –繼承 class 獲取的 field ,實際上還是定義在父類中的; –而繼承 trait獲取的 field,就直接被新增到子類中。

3、trait中定義抽象的方法和field

Scala中的trait也能定義抽象方法和抽象field, 而trait中的具體方法也能基於抽象field編寫 繼承trait的類,則必須覆蓋抽象方法和抽象field,提供具體的值;

package com.fgm.traits

trait SayHello {
  val msg:String
  def sayHello(name: String) = println(msg + name)
  def say:Unit
}

class TraitTest02(val name: String) extends SayHello{
  //必須覆蓋抽象 field
  val msg = "Hello"
  def makeFriends(other: TraitTest02) = {
    this.sayHello(other.name)
    println("I'm " + this.name + ", I want to make friends with you")
  }
  //必須覆蓋抽象方法
  override def say: Unit = {
    println("this is the method to override abstract method ")
  }
}
object TraitTest02{
  def main(args: Array[String]) {
    val p1=new TraitTest02("Tom")
    val p2=new TraitTest02("Jerry")
    p1.makeFriends(p2)
    //輸出結果:HelloJerry
	//I'm Tom, I want to make friends with you
  }
}

4、在例項物件中指定混入某個trait

可在建立類的物件時,使用 with 關鍵字為該物件指定混入某個trait。 且只有混入了trait的物件才具有trait中的方法,而其他該類的物件則沒有;

package com.fgm.traits

trait LogTrait {
  // 該方法為實現的具體方法
  def log(msg: String) = {}
}
trait MyLogger extends LogTrait{
  // 覆蓋 log() 方法
  override def log(msg: String) = println("log: " + msg)
}

class MixTrait(val name: String) extends LogTrait {
  def sayHello = {
    println("Hi, I'm " + this.name)
    log("I'm the trait method")
  }
}
object MixTrait{
  def main(args: Array[String]) {
    
    val tom= new MixTrait("Tom")
    //結果為:Hi, I'm Tom
    tom.sayHello 
    
    // 使用 with 關鍵字,指定混入MyLogger trait
    val rose = new MixTrait("Rose") with MyLogger
    // 輸出結果為:Hi, I'm Rose
    // 輸出結果為:log: I'm the trait method
    rose.sayHello
    
  }
}

5、trait 呼叫鏈

Scala中支援讓類繼承多個trait後,可依次呼叫多個trait中的同一個方法,只要讓多個trait中的同一個方法,在最後都依次執行 super 關鍵字即可; 類中呼叫多個trait中都有的這個方法時,首先會從最右邊的trait的方法開始執行,然後依次往左執行,形成一個呼叫鏈條; 這是設計模式中責任鏈模式的一種具體實現;

6、混合使用 trait 的具體方法和抽象方法

在 trait 中,可以混合使用具體方法和抽象方法:可以讓具體方法依賴於抽象方法,而抽象方法則可放到繼承 trait的子類中去實現; 這是設計模式中的模板設計模式的體現;

trait ValidTrait {
 //抽象方法
  def getName: String     
//具體方法,具體方法的返回值依賴於抽象方法
                        
 def valid: Boolean = {"Tom".equals(this.getName)
  }
}
class PersonForValid(val name: String) extends ValidTrait {
  def getName: String = this.name
}

object PersonForValid{
  def main(args: Array[String]): Unit = {
    val person = new PersonForValid("Rose")
    println(person.valid)
  }
}

7、trait的構造機制

在Scala中,trait也是有構造程式碼的,即在trait中,不包含在任何方法中的程式碼; 繼承了trait的子類,其構造機制如下: 父類的建構函式先執行, class 類必須放在最左邊; 多個trait從左向右依次執行; 構造trait時,先構造父 trait,如果多個trait繼承同一個父trait,則父trait只會構造一次; 所有trait構造完畢之後,子類的建構函式最後執行。

class Person {
  println("Person's constructor!")
}
trait Logger {
  println("Logger's constructor!")
}
trait MyLogger extends Logger {
  println("MyLogger's constructor!")
}
trait TimeLogger extends Logger {
  println("TimeLogger's contructor!")
}
class Student extends Person with MyLogger with TimeLogger {
  println("Student's constructor!")
  }
object exe {
  def main(args: Array[String]): Unit = {
    val student = new Student
    //執行結果為:
//      Person's constructor!
//      Logger's constructor!
//      MyLogger's constructor!
//      TimeLogger's contructor!
//      Student's constructor!
  }
}

該案例中: (1)在構建class類Student的物件時,其父類Person的建構函式先執行。 (2)之後MyLogger 和TimeLogger 按照順序從左到右依次執行,也就是先構造trait MyLogger ,由於MyLogger 繼承了Logger,所以,在構造MyLogger 時,先構造它的父trait,也就是Logger的構造方法接著執行; (3)在MyLogger 的父trait Logger 構造執行之後,MyLogger 的建構函式開始執行; (4)接下來,按照從左到右的順序,執行TimeLogger ,TimeLogger 也有父trait ,但它的父trait和MyLogger的父trait相同,按照“多個trait繼承同一個父trait,則父trait只會構造一次”的原則,這裡不再執行Logger的建構函式,所以直接執行TimeLogger 的建構函式。 (5)所有trait構造完畢之後,子類的建構函式最後執行。此時發現所有trait的建構函式均已執行完畢,所以此時執行子類Student的建構函式。

8、trait 繼承 class

在Scala中trait 也可以繼承 class,此時這個 class 就會成為所有繼承該 trait 的子類的超級父類.

9、一些已定義好的trait

Ordered:Ordered特質擴充套件自java的Comparable介面,Ordered特質用於排序,為了使用它,首先將其混入類中,然後實現一個compare方法。 原始碼參考:

trait Ordered[A] extends Any with java.lang.Comparable[A] {

  /** Result of comparing `this` with operand `that`.
   *
   * Implement this method to determine how instances of A will be sorted.
   *
   * Returns `x` where:
   *
   *   - `x < 0` when `this < that`
   *
   *   - `x == 0` when `this == that`
   *
   *   - `x > 0` when  `this > that`
   *
   */
  def compare(that: A): Int

  /** Returns true if `this` is less than `that`
    */
  def <  (that: A): Boolean = (this compare that) <  0

  /** Returns true if `this` is greater than `that`.
    */
  def >  (that: A): Boolean = (this compare that) >  0

  /** Returns true if `this` is less than or equal to `that`.
    */
  def <= (that: A): Boolean = (this compare that) <= 0

  /** Returns true if `this` is greater than or equal to `that`.
    */
  def >= (that: A): Boolean = (this compare that) >= 0

  /** Result of comparing `this` with operand `that`.
    */
  def compareTo(that: A): Int = compare(that)
}

object Ordered {
  /** Lens from `Ordering[T]` to `Ordered[T]` */
  implicit def orderingToOrdered[T](x: T)(implicit ord: Ordering[T]): Ordered[T] =
    new Ordered[T] { def compare(that: T): Int = ord.compare(x, that) }
}

Ordering Application APP