1. 程式人生 > >Scala中面向物件程式設計之trait

Scala中面向物件程式設計之trait

1.1將trait作為介面使用
Scala中的trait是一種特殊的概念;
首先先將trait作為介面使用,此時的trait就與Java中的介面 (interface)非常類似;
在trait中可以定義抽象方法,就像抽象類中的抽象方法一樣,只要不給出方法的方法體即可;
類可以使用extends關鍵字繼承trait,注意,這裡不是 implement,而是extends ,在Scala中沒有 implement 的概念,無論繼承類還是trait,統一都是 extends;
類繼承後,必須實現其中的抽象方法,實現時,不需要使用 override 關鍵字;
Scala不支援對類進行多繼承,但是支援多重繼承 trait,使用 with 關鍵字即可。
舉例說明:
package cn.itcast.triat

trait HelloTrait {
def sayHello(): Unit
}
trait MakeFriendsTrait {
def makeFriends(c: Children): Unit
}
//多重繼承 trait
class Children(val name: String) extends HelloTrait with MakeFriendsTrait with Cloneable with Serializable{
def sayHello() =println("Hello, " + this.name)
def makeFriends(c: Children) = println("Hello, my name is " +

this.name + ", your name is " + c.name)
}
object Children{
def main(args: Array[String]) {
val c1=new Children(“tom”)
val c2=new Children(“jim”)
c1.sayHello()//Hello, tom
c1.makeFriends(c2)//Hello, my name is tom, your name is jim
}
}

1.2. 在trait中定義具體的方法
Scala中的trait不僅可以定義抽象方法,還可以定義具體的方法,此時 trait 更像是包含了通用方法的工具,可以認為trait還包含了類的功能。

舉例說明:
package cn.itcast.triat
/**

  • 比如 trait 中可以包含很多子類都通用的方法,例如列印日誌或其他工具方法等等。
  • spark就使用trait定義了通用的日誌列印方法;
    /
    trait Logger {
    def log(message: String): Unit = println(message)
    }
    /
    *
    誰呼叫的誰就是this屬性
    */
    class PersonForLog(val name: String) extends Logger {
    def makeFriends(other: PersonForLog) = {
    println("Hello, " + other.name + "! My name is " + this.name + “, I miss you!!”)
    this.log("makeFriends method is invoked with parameter PersonForLog[name = " + other.name + “]”)
    }
    }
    object PersonForLog{
    def main(args: Array[String]) {
    val p1=new PersonForLog(“jack”)
    val p2=new PersonForLog(“rose”)
    p1.makeFriends(p2)
    //Hello, rose! My name is jack, I miss you!!
    //makeFriens method is invoked with parameter PersonForLog[name = rose]
    }
    }
    1.3. 在trait中定義具體field
    Scala 中的 trait 可以定義具體的 field,此時繼承 trait 的子類就自動獲得了 trait 中定義的 field;
    但是這種獲取 field 的方式與繼承 class 的是不同的。 如果是繼承 class 獲取的 field ,實際上還是定義在父類中的;而繼承 trait獲取的 field,就直接被新增到子類中了。
    舉例說明:

package cn.itcast.triat

trait PersonForField {
val age:Int=50
}

//繼承 trait 獲取的field直接被新增到子類中
class StudentForField(val name: String) extends PersonForField {
def sayHello = println("Hi, I’m " + this.name + ", my age is "+ age)
}

object StudentForField{
def main(args: Array[String]) {
val s=new StudentForField(“tom”)
s.sayHello
}
}

1.4. 在trait中定義抽象field
Scala中的trait也能定義抽象field, 而trait中的具體方法也能基於抽象field編寫;
繼承trait的類,則必須覆蓋抽象field,提供具體的值;
舉例說明:
package cn.itcast.triat

trait SayHelloTrait {
val msg:String
def sayHello(name: String) = println(msg + ", " + name)
}

class PersonForAbstractField(val name: String) extends SayHelloTrait {
//必須覆蓋抽象 field
val msg = “Hello”
def makeFriends(other: PersonForAbstractField) = {
this.sayHello(other.name)
println("I’m " + this.name + “, I want to make friends with you!!”)
}
}
object PersonForAbstractField{
def main(args: Array[String]) {
val p1=new PersonForAbstractField(“Tom”)
val p2=new PersonForAbstractField(“Rose”)
p1.makeFriends(p2)
}
}

1.5. 在例項物件指定混入某個trait
可在建立類的物件時,為該物件指定混入某個trait,且只有混入了trait的物件才具有trait中的方法,而其他該類的物件則沒有;
在建立物件時,使用 with 關鍵字指定混入某個 trait;
舉例說明:
package cn.itcast.triat

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

class PersonForMixTraitMethod(val name: String) extends LoggedTrait {
def sayHello = {
println("Hi, I’m " + this.name)
log(“sayHello method is invoked!”)
}
}
object PersonForMixTraitMethod{
def main(args: Array[String]) {
val tom= new PersonForMixTraitMethod(“Tom”).sayHello //結果為:Hi, I’m Tom
// 使用 with 關鍵字,指定混入MyLogger trait
val rose = new PersonForMixTraitMethod(“Rose”) with MyLogger
rose.sayHello
// 結果為: Hi, I’m Rose
// 結果為: log: sayHello method is invoked!
}
}
1.6. trait 呼叫鏈
Scala中支援讓類繼承多個trait後,可依次呼叫多個trait中的同一個方法,只要讓多個trait中的同一個方法,在最後都依次執行 super 關鍵字即可;
類中呼叫多個trait中都有的這個方法時,首先會從最右邊的trait的方法開始執行,然後依次往左執行,形成一個呼叫鏈條;
這種特性非常強大,其實就是設計模式中責任鏈模式的一種具體實現;
案例說明:
package cn.itcast.triat

trait HandlerTrait {
def handle(data: String) = {println(“last one”)}
}
trait DataValidHandlerTrait extends HandlerTrait {
override def handle(data: String) = {
println("check data: " + data)
super.handle(data)
}
}
trait SignatureValidHandlerTrait extends HandlerTrait {
override def handle(data: String) = {
println("check signature: " + data)
super.handle(data)
}
}
class PersonForRespLine(val name: String) extends SignatureValidHandlerTrait with DataValidHandlerTrait {
def sayHello = {
println("Hello, " + this.name)
this.handle(this.name)
}
}
object PersonForRespLine{
def main(args: Array[String]) {
val p=new PersonForRespLine(“tom”)
p.sayHello
//執行結果:
// Hello, tom
// check data: tom
// check signature: tom
// last one
}
}

1.7. 混合使用 trait 的具體方法和抽象方法
在 trait 中,可以混合使用具體方法和抽象方法;
可以讓具體方法依賴於抽象方法,而抽象方法則可放到繼承 trait的子類中去實現;
這種 trait 特性,其實就是設計模式中的模板設計模式的體現;
舉例說明:
package cn.itcast.triat

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)
}
}

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

package cn.itcast.triat

class Person_One {
println(“Person’s constructor!”)
}
trait Logger_One {
println(“Logger’s constructor!”)
}
trait MyLogger_One extends Logger_One {
println(“MyLogger’s constructor!”)
}
trait TimeLogger_One extends Logger_One {
println(“TimeLogger’s contructor!”)
}
class Student_One extends Person_One with MyLogger_One with TimeLogger_One {
println(“Student’s constructor!”)
}
object exe_one {
def main(args: Array[String]): Unit = {
val student = new Student_One
//執行結果為:
// Person’s constructor!
// Logger’s constructor!
// MyLogger’s constructor!
// TimeLogger’s contructor!
// Student’s constructor!
}
}

1.9. trait 繼承 class
在Scala中trait 也可以繼承 class,此時這個 class 就會成為所有繼承該 trait 的子類的超級父類。
舉例說明:
package cn.itcast.triat

class MyUtil {
def printMsg(msg: String) = println(msg)
}
trait Logger_Two extends MyUtil {
def log(msg: String) = this.printMsg("log: " + msg)
}
class Person_Three(val name: String) extends Logger_Two {
def sayHello {
this.log("Hi, I’m " + this.name)
this.printMsg("Hello, I’m " + this.name)
}
}
object Person_Three{
def main(args: Array[String]) {
val p=new Person_Three(“Tom”)
p.sayHello
//執行結果:
// log: Hi, I’m Tom
// Hello, I’m Tom
}
}