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