这里,紧接着上一篇博文scala学习笔记2的特质Trait来写,博主在学习scala和Spark的时候,觉得Trait这一特性非常重要,所以便有了此篇。
1.trait基础知识
1.1 将trait作为接口使用
首先我们可以将Trait作为接口来使用,此时的Triat就与Java中的接口(interface)非常类似。在triat中可以定义抽象方法,就与抽象类中的抽象方法一样,只要不给出方法的具体实现即可
类可以使用extends关键字继承trait,注意,这里不是implement,而是extends,在scala中没有implement的概念,无论继承类还是trait,统一都是extends
类继承trait后,必须实现其中的抽象方法,实现时不需要使用override关键字。scala不支持对类进行多继承,但是支持多重继承trait,使用with关键字即可
1.2 在trait中定义具体方法,具体字段
Scala中的Trait可以不是只定义抽象方法,还可以定义具体方法,具体字段。
trait CarId{
// var id: Int //这里id没有初始化 所以在子类继承该trait时,必须覆盖抽象field,提供具体的值
var id=10
def currentId(): Int //定义了一个抽象方法
def test(msg:String): Unit ={
println(msg)
}
}
class BYDCarId extends CarId{ //使用extends关键字
// override var id = 10000 //BYD汽车编号从10000开始
def currentId(): Int = {id += 1; id} //返回汽车编号
}
class BMWCarId extends CarId{ //使用extends关键字
// override var id = 20000 //BMW汽车编号从10000开始
def currentId(): Int = {id += 12; id} //返回汽车编号
}
object TraitLearn {
def main(args: Array[String]): Unit = {
val myCarId1 = new BYDCarId()
val myCarId2 = new BMWCarId()
printf("My first CarId is %d.\n",myCarId1.currentId)
myCarId1.test("123")
printf("My second CarId is %d.\n",myCarId2.currentId)
myCarId2.test("abcd")
}
}
trait中定义具体字段:
- Scala中的Triat可以定义具体field,此时继承trait的类就自动获得了trait中定义的field
- 但是这种获取field的方式与继承class是不同的:如果是继承class获取的field,实际是定义在父类中的; 而继承trait获取的field,就直接被添加到了类中
1.3 在trait中定义抽象字段
简单理解,就是在trait里面声明的成员属性未赋初值,所以在class继承trait 如果要调用trait中的成员属性 必须复写给器赋初始值。
2.trait高级知识
2.1 为实例对象混入trait
有时我们可以在创建类的对象时,指定该对象混入某个trait, 这样,就只有这个对象混入该trait的方法,而类的其他对象则没有。比如下面这个例子:
object TraitLearn03 {
trait Log {
def log(msg: String){}
//编程时注意 这必须加上大括号 表示该方式实现了 非抽象方法
}
trait MyLog extends Log {
override def log(msg: String) = println("log: " + msg)
}
class Per(val name: String) extends Log {
def sayHello: Unit = {
print("Hi, I'm " + name+"; ")
log("12345")
}
}
def main(args: Array[String]): Unit = {
val p1 = new Per("xjh")
p1.sayHello
println()
val p2 = new Per("kobe") with MyLog //这里with MyLog指的是对象p2混入trait MYLog
p2.sayHello
}
}
因为对象p2在初始化的时候混入了trait MyLog,所以p2。sayHello的输出与p1会不同。
Hi, I'm xjh;
Hi, I'm kobe; log: 12345
2.2 trait调用链
Scala中支持让类继承多个trait后,依次调用多个trait中的同一个方法,只要让多个trait的同一个方法中,在最后都执行super.方法即可
类中调用多个trait中都有的这个方法时,首先会从最右边的trait的方法开始执行,然后依次往左执行,形成一个调用链条
这种特性非常强大,其实就相当于设计模式中的责任链模式的一种具体实现依赖
object TraitLearn04 {
trait Handle{
def handle(data:String){}
}
trait DataHandle extends Handle{
override def handle(data: String): Unit = {
println("check data: "+data)
super.handle(data)
}
}
trait SignHandle extends Handle{
override def handle(data: String): Unit = {
println("check sign: "+data)
super.handle(data)
}
}
class Pree(val name:String) extends SignHandle with DataHandle{
//trait从右往左 依次执行
def sayHello: Unit ={
println("Hello ,"+name)
handle(name)
}
}
def main(args: Array[String]): Unit = {
val p=new Pree("xjh")
p.sayHello
}
}
Hello ,xjh
check data: xjh
check sign: xjh
注意:在这个例子中,特质Handle的两个字特质SignHandle,DataHandle在重写父特质handle方法时 都在方法里面最后加上的super.方法。我们都知道super.方法名 是显示的调用父类中的方法。在这里 因为class继承多个trait是从右往左,所以实质上这里SignHandle对应的handle方法里面不写super.方法名 也能输出同样的结果。但是为了保持代码的完整性和可移植性,建议都写上。
2.3 在trait中覆盖抽象方法
在trait中,是可以覆盖父trait的抽象方法的。但是覆盖时,如果使用了super.方法的代码,则无法通过编译。因为super.方法就会去掉用父trait的抽象方法,此时子trait的该方法还是会被认为是抽象的。此时如果要通过编译,就得给子trait的方法加上abstract override修饰
trait Logger {
def log(msg: String)
}
trait MyLogger extends Logger {
abstract override def log(msg: String) { super.log(msg) }
}
2.4 混合使用trait的具体方法和抽象方法
在trait中,可以混合使用具体方法和抽象方法
可以让具体方法依赖于抽象方法,而抽象方法则放到继承trait的类中去实现
这种trait其实就是设计模式中的模板设计模式的体现
object TraitLearn06 {
trait Valid {
def getName: String
def valid: Boolean = getName == "xjh"
}
class Ptest(val name: String) extends Valid {
println(valid)
override def getName: String = name
}
def main(args: Array[String]): Unit = {
val p = new Ptest("xjh")
}
}
2.5 trait的构造机制
在Scala中,trait也是有构造代码的,也就是trait中的,不包含在任何方法中的代码
而继承了trait的类的构造机制如下:
- 1、父类的构造函数执行;
- 2、trait的构造代码执行,多个trait从左到右依次执行;
- 3、构造trait时会先构造父trait,如果多个trait继承同一个父trait,则父trait只会构造一次;
- 4、所有trait构造完毕之后,子类的构造函数执行
这一部分面向对象语言都是大同小异,这里便不再编写demo记性测试
2.6 让trait继承类
在Scala中,trait也可以继承自class,此时这个class就会成为所有继承该trait的类的父类
object TraitLearn05 {
class MyUtil{
def printMessage(msg:String)=println(msg)
}
trait LoUtil extends MyUtil{
def log(msg:String)=printMessage("log: "+msg)
}
class Prere(val name:String) extends LoUtil{
def sayHello: Unit ={
log("Hi, I'm "+name)
printMessage("Hi,I'm "+name)
}
}
def main(args: Array[String]): Unit = {
val p=new Prere("jiahao")
p.sayHello
}
}