Spark基础-scala学习(三、Trait)

时间:2021-11-18 10:30:07

面向对象编程之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 = Person@41eb94bc 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 = Person@6a6e9289 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 = Student@12765ebd 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 = Person@67cd193d 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 = Person@20307cb9 scala> p1.sayHello
Hi,I'm leo scala> val p2 = new Person("jack") with MyLogger
p2: Person with MyLogger = $anon$1@192800d 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 = Person@5fefb35c 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 = Person@30749de8 scala> p.getName
res10: String = leo scala> val p2 = new Person("Tom")
false
p2: Person = Person@622a4589 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 = Student@22caeb7f

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 = $anon$1@6ce7fb0c
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 = Person@16c87aa2
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 = Person@3219b550

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 = Person@3685cd73 scala> p.sayHello
log: Hi,I'm Jike
Hi,I'm Jike