1. 程式人生 > >Spark基礎-scala學習(三)

Spark基礎-scala學習(三)

面向物件程式設計之Trait

trait基礎知識

  1. 將trait作為介面使用
  2. 在trait中定義具體方法
  3. 在trait中定義具體欄位
  4. 在trait中定義抽象欄位

trait高階知識

  1. 為例項物件混入trait
  2. trait呼叫鏈
  3. 在trait中覆蓋抽象方法
  4. 混合使用trait的具體方法和抽象方法
  5. trait的構造機制
  6. trati欄位的初始化
  7. 讓trait繼承類

將trait作為介面使用

  1. trait作為介面,和java中的介面非常類似
  2. 在triat中可以定義抽象方法,就與抽象類中的抽象方法一樣,只要不給出方法的具體實現即可
  3. 類可以使用extends關鍵字繼承trait,注意,這裡不是implement,而是extends,在scala中沒有implement的概念,無論繼承類還是trait,統一都是extends
  4. 類繼承trait後,必須實現其中的抽象方法,實現時不需要使用override關鍵字
  5. scala不支援對類進行多繼承,而是支援多重繼承trait,使用with關鍵字即可
scala> :paste
// Entering paste mode (ctrl-D to finish)

trait HelloTrait{
 def sayHello(name:String)
}
trait MakeFriendsTrait{
 def makeFriend(p:Person)
}
class Person(val name:String) extends HelloTrait with MakeFriendsTrait with Cloneable with Serializable
{
 def sayHello(name:String) = println("Hello," + name)
 def makeFriend(p:Person) = println("Hello,my name is "+name +",your name is "+p.name)
}

// Exiting paste mode, now interpreting.

defined trait HelloTrait
defined trait MakeFriendsTrait
defined class Person

scala> val p = new Person("Tom")
p: Person = 
[email protected]
scala> p.makeFriend(p) Hello,my name is Tom,your name is Tom scala> p.sayHello("jike") Hello,jike

在Trait中定義具體的方法

  1. scala中的Triat可以不是隻定義抽象方法,還可以定義具體方法,此時trait更像是包含了通用工具方法的東西
  2. 就想trait的功能混入了類
  3. 舉例來說,trait中可以包含一些很多類都通用的功能方法,比如列印日誌等等,spark中就使用了trait來定義了通用的日誌列印方法
scala> :paste
// Entering paste mode (ctrl-D to finish)

trait Logger{
 def log(message :String) = println(message)
}
class Person(val name:String) extends Logger{
 def makeFriends(p:Person){
  println("Hi,I'm "+name+",I'm glad to make friends with you," + p.name)
  log("makeFriends method is invoked with parameter Person[name="+p.name+"]")
 }
}

// Exiting paste mode, now interpreting.

defined trait Logger
defined class Person

scala> val p = new Person("Tom")
p: Person = 
[email protected]
scala> p.makeFriends(p) Hi,I'm Tom,I'm glad to make friends with you,Tom makeFriends method is invoked with parameter Person[name=Tom]

在trait中定義具體欄位

  1. scala中trait可以定義具體field,此時繼承trait的類就可以自動獲得trait中定義的field
  2. 但是這種獲取field的方式與繼承class是不同的:如果是繼承class獲得的field,實際是定義在父類中的;而繼承trait獲取的field,就直接被新增到類中
scala> :paste
// Entering paste mode (ctrl-D to finish)

trait Person{
 val eyeNum:Int = 2
}
class Student(val name:String) extends Person{
 def sayHello = println("Hi,I'm "+name+",I have "+eyeNum+" eyes")
}

// Exiting paste mode, now interpreting.

defined trait Person
defined class Student

scala> val s = new Student("Tom")
s: Student = [email protected]

scala> s.sayHello
Hi,I'm Tom,I have 2 eyes

在Trait中定義抽象欄位

  1. scala中的Trait可以定義抽象field,而trait中的具體方法則可以基於抽象field來編寫
  2. 但是繼承trait的類,則必須覆蓋抽象field,提供具體的值
scala> :paste
// Entering paste mode (ctrl-D to finish)

trait SayHello{
 val msg:String
 def sayHello(name:String) = println(msg+","+name)
}
class Person(val name:String) extends SayHello{
 val msg:String = "hello"
 def makeFriends(p:Person){
  sayHello(p.name)
  print("I'm "+name+",I want to make friends with you!")
 }
}

// Exiting paste mode, now interpreting.

defined trait SayHello
defined class Person

scala> val p = new Person("Tom")
p: Person = [email protected]

scala> p.msg
res4: String = hello

scala> p.makeFriends(p)
hello,Tom
I'm Tom,I want to make friends with you!

為例項混入trait

  1. 有時候我們可以在建立類的物件時,指定該物件混入某個trait,這樣,就只有這個物件混入該trait的方法,而類的其他物件則沒有
scala> :paste
// Entering paste mode (ctrl-D to finish)

trait Logged{
 def log(msg:String){}
}
trait MyLogger extends Logged{
 override def log(msg:String){println("log:"+msg)}
}
class Person(val name:String) extends Logged{
 def sayHello{println("Hi,I'm "+name);log("sayHello is invoked!")}
}


// Exiting paste mode, now interpreting.

defined trait Logged
defined trait MyLogger
defined class Person

scala> val p1 = new Person("leo")
p1: Person = [email protected]

scala> p1.sayHello
Hi,I'm leo

scala> val p2 = new Person("jack") with MyLogger
p2: Person with MyLogger = [email protected]

scala> p2.sayHello
Hi,I'm jack
log:sayHello is invoked!

trait呼叫鏈

  1. scala中支援多個trait,一次呼叫多個trait中的同一個方法,只要讓多個trait的同一個方法中,在最後都執行super.方法即可
scala> :paste
// Entering paste mode (ctrl-D to finish)

trait Handler{
 def handle(data:String){}
}
trait DataValidHandler extends Handler{
 override def handle(data:String){
  println("check data:" +data)
  super.handle(data)
 }
}
trait SignatureValidHandler extends Handler {
 override def handle(data:String){
  println("check signature: "+data)
  super.handle(data)
 }
}
class Person(val name:String) extends SignatureValidHandler with DataValidHandler{
 def sayHello = { println("Hello, " + name);handle(name)}
}

// Exiting paste mode, now interpreting.

defined trait Handler
defined trait DataValidHandler
defined trait SignatureValidHandler
defined class Person

scala> val p1 = new Person("Tom")
p1: Person = [email protected]

scala> p1.sayHello
Hello, Tom
check data:Tom
check signature: Tom

在trait中覆蓋抽象方法

  1. 在trait中,是可以覆蓋父trait的抽象方法的
  2. 但是覆蓋時,如果使用了super.方法的程式碼,則無法通過編譯。因為super.方法就會去呼叫父trait的抽象方法,此時子trait的該方法還是會被認為是抽象的
  3. 此時如果要通過編譯,就得給子trait的方法加上abstract override修飾
trait Logger{
 def log(msg:String)
}
trait MyLogger extends Logger{
 abstract override def log(msg:String) {super.log(msg)}
}

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

  1. 在trait中,可以混合使用具體方法和抽象方法
  2. 可以讓具體方法依賴於抽象方法,而抽象方法則放到繼承trait的類中去實現
  3. 這種trait其實就是設計模式中的模板設計模式的體現
scala> :paste
// Entering paste mode (ctrl-D to finish)

trait Valid{
 def getName:String
 def valid:Boolean = {
  getName == "leo"
 }
}
class Person(val name:String) extends Valid{
 println(valid)
 def getName = name
}

// Exiting paste mode, now interpreting.

defined trait Valid
defined class Person

scala> val p = new Person("leo")
true
p: Person = [email protected]

scala> p.getName
res10: String = leo

scala> val p2 = new Person("Tom")
false
p2: Person = [email protected]

scala> p2.getName
res11: String = Tom

trait的構造機制

  1. 在scala中,trait也是有構造程式碼的,也就是trait中,不包含在任何方法中的程式碼
  2. 而繼承了trait的類的構造機制如下
    1. 父類的建構函式執行
    2. trait的構造程式碼執行,多個trait從左到右依次執行
    3. 構造trait時會先構造父trait,如果多個trait繼承同一個父trait,則父trait只會構造一次
    4. 所有trait構造完畢之後,子類的建構函式執行
scala> :paste
// Entering paste mode (ctrl-D to finish)

class Person{println("Person constructor")}
trait Logger{println("Logger constructor")}
trait MyLogger extends Logger{println("MyLogger constructor")}
trait TimeLogger extends Logger{println("TimeLogger constructor")}
class Student extends Person with MyLogger with TimeLogger{
 println("Student constructor")
}

// Exiting paste mode, now interpreting.

defined class Person
defined trait Logger
defined trait MyLogger
defined trait TimeLogger
defined class Student

scala> val s = new Student
Person constructor
Logger constructor
MyLogger constructor
TimeLogger constructor
Student constructor
s: Student = [email protected]

trait field的初始化

  1. 在scala中,trait是沒有接受引數的建構函式的,這是trait與clas的唯一區別,但是如果需求就是要trait能夠對field進行初始化,我們可以使用scala中非常特殊的一種高階特性--提前定義
scala> :paste
// Entering paste mode (ctrl-D to finish)

trait SayHello{
 val msg:String
 println(msg:String)
}
class Person
val p = new {
 val msg:String = "init"
} with Person with SayHello

// Exiting paste mode, now interpreting.

init
defined trait SayHello
defined class Person
p: Person with SayHello = [email protected]
scala> :paste
// Entering paste mode (ctrl-D to finish)

class Person extends{
 val msg:String = "init"
} with SayHello{}

// Exiting paste mode, now interpreting.

defined class Person

scala> val p2 = new Person
init
p2: Person = [email protected]
scala> :paste
// Entering paste mode (ctrl-D to finish)

trait SayHello{
 lazy val msg:String = null
 println(msg.toString)
}
class Person extends SayHello{
 override lazy val msg:String = "init"
}

// Exiting paste mode, now interpreting.

defined trait SayHello
defined class Person

scala> val p3 = new Person
init
p3: Person = [email protected]

trait繼承class

  1. 在scala中,trait也可以繼承自class,此時這個class就會為所有繼承該trait的類的父類
scala> :paste
// Entering paste mode (ctrl-D to finish)

class MyUtil{
 def printMessage(msg:String) = println(msg)
}
trait Logger extends MyUtil{
 def log(msg:String) = printMessage("log: "+msg)
}
class Person(val name:String) extends Logger{
 def sayHello{
  log("Hi,I'm "+name)
  printMessage("Hi,I'm "+name)
 }
}

// Exiting paste mode, now interpreting.

defined class MyUtil
defined trait Logger
defined class Person

scala> val p = new Person("Jike")
p: Person = [email protected]

scala> p.sayHello
log: Hi,I'm Jike
Hi,I'm Jike