天天看點

10、Scala特質trait

本文介紹Scala中的trait特質。

1、基本知識

特質 (Traits) 用于在類 (Class)之間共享程式接口 (Interface)和字段 (Fields)。 它們類似于Java 8的接口。

即:特質裡寫了一些方法 和 字段,而這些是可以被所有類所共用、共享的。

這就像在類之間搭建了橋梁一樣(或者打上了相同的标記),使得所有用了這個特質的類都可以有相同的特質中的方法和字段。

當然一個類可以使用多個特質,而不僅限于一個特質。

類和對象 (Objects)可以擴充特質,但是特質不能被執行個體化,是以特質沒有參數。

特質使用關鍵詞trait定義。

某種程度上看特質trait和類class很像,它們都不能獨立運作,類必須被執行個體化,而特質必須被extends擴充(或繼承)。

但特質比類更友善作為共享接口的地方就在于,它可以定義沒有任何方法、隻是約定了輸入/輸出參數類型的函數。

而這就可以作為類型/模式比對的模版使用。

2、trait的定義方法

2.1 定義沒有執行方法的trait

//in Scala

trait Cardetails{
	def details(d:String):String
}
           

2.2 定義有執行方法的trait

//in scala
trait detcar{
 def readdetails(d:String):String =
   Source.fromString(d).mkString
}
           

3、trait的使用方法

3.1 類使用extends來繼承沒有定義任何執行方法的特質

//in scala

//定義一個特質
trait Cardetails{
	def details(d:String):String
}

//定義一個類內建該特質,并在類中重寫特質中的方法
class Cardet extends Cardetails {
  import scala.io.Source
  override def details(source:String) = {
	Source.fromString(source).mkString
  }
}

object car {
  def main(args:Array[String]){
	val c1 = new Cardet //執行個體化類
	println(c1.details("Car details are being displayed")) //調用類中的方法
	println(c1.isInstanceOf[Cardetails])  //驗證下類是不是繼承了這個特質
  }
}
           
10、Scala特質trait

3.2 一個類即繼承了一個類,同時又繼承了一個特質,使用extends  (類) with(特質)文法

//in scala
import scala.io.Source

//定義一個特質 detcar
trait detcar{
  def readdetails(d:String):String =
    Source.fromString(d).mkString +": this from trait"
}

//定義一個有參數的類Car
class Car(var cname:String, var cno:Int){
  def details = cname+" "+cno
}

//定義一個有參數的類Alto,該類繼承了類Car,同時擁有特質detcar。
class Alto( cname:String, cno:Int,var color:String) extends Car(cname,cno) with detcar{

  //子類重寫了父類的方法details,并且在重寫的方法中引用了特質的方法
  override def details = {
    val det = readdetails(color)
    cname+"\n"+cno+"\n"+"Color:"+color +"\n" +det
  }
}

object Student {
  def main(args:Array[String]){
    val a1 = new Alto("Alto",34,"Black")
    println(a1.details)
  }
}
           
10、Scala特質trait

3.3 特質trait的多繼承

特質可以繼承特質,類也可以繼承多個特質。

object TraitOrder extends App {

  trait Logger {
    println("Logger")
  }

 //一個trait 繼承另一個trait
  trait FileLogger extends Logger {
    println("FileLogger")
  }

  trait Closable {
    println("Closable")
  }
  class Person{
    println("Constructing Person...")
  }

//一個類繼承了父類,又繼承了多個特質trait
  class Student extends Person with FileLogger with Closable {
    println("Constructing Student ...")
  }

  new Student
}
           

3.4 多個特質的繼承順序

準則:

  1. 如果有超類,則先調用超類的函數。
  2. 如果混入的trait有父trait,它會按照繼承層次先調用父trait的構造函數。
  3. 如果有多個父trait,則按順序從左到右執行。
  4. 所有父類構造函數和父trait被構造完之後,才會構造本類的構造函數。

這個準則依次對應到3.3中的代碼為:

1、Student有父類Person

2、特質FileLogger有父特質 Logger

3、Student同時繼承了兩個父特質FileLogger 、Closable,繼承順序是從左到右執行

4、父類Person、特質Logger\FileLogger\Closable構造完後,Student類的構造函數才會開始

4、比較和選擇使用trait 、abstract class的場景

在有可重複使用的方法、收集器的場景中,我們該怎麼判斷是使用trait 還是 abstract class呢?

以下4條基線可以參考:

  • 如果運作效率是你關注的一個名額,那麼建議使用抽象類。 因為Traits 被編譯為接口,可能會有輕微的性能開銷。
  • 如果你需要繼承一些JAVA代碼,那麼最好使用抽象類,因為特征沒有 Java 模拟(analog),從特征繼承就會很奇怪。
  • 如果行為不可重用,建議使用具體類。
  • 如果行為在多個不相關的類中重用,則可以使用特征。

繼續閱讀