专栏首页Java工程师成长之路Spark基础-scala学习(三、Trait)

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

面向对象编程之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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Spark基础-scala学习(一、入门)

    老梁
  • sharding sphere 4.0.0-RC1版本 按年分表实战

    需要对日志表进行按时间划分表,由于用于后台系统,日志量预估不会太大,因此按年划分表

    老梁
  • ElasticSearch5.0之后的改变

    老梁
  • Petya真的没那么简单!北约呼吁发起联合调查,US-CERT发布警报

    北约(NATO)认为Petya大规模袭击可能是国家层面的攻击。Petya和WannaCry这种严重的网络安全问题需要国际社会联合响应。 NATO认为近期大规模传...

    FB客服
  • CentOS7下安装PostgreSQL12

    PostgreSQL是一个功能强大的开源数据库系统。经过长达15年以上的积极开发和不断改进,PostgreSQL已在可靠性、稳定性、数据一致性等获得了业内极高的...

    yuanfan2012
  • SqlServer注意事项总结,高级程序员必背!

    想成为一个高级程序员,数据库的使用是必须要会的。而数据库的使用纯熟程度,也侧面反映了一个开发的水平。

    Kiba518
  • Android开发之基于AndroidStudio环境搭建和工程创建

    断断续续的学习安卓也有一段时间了。因为之前是搞iOS开发的, 之前有关iOS的博客请看《我的iOS开发系列博文》、《我的Objective-C系列文章》和《窥探...

    lizelu
  • 共享在线网络磁盘目录程序Z-File,页面简洁美观

    此项目是一个在线文件目录的程序, 支持各种对象存储和本地存储, 使用定位是个人放常用工具下载, 或做公共的文件库. 不会向多账户方向开发.

    用户1444933
  • 推荐一个c++小巧开源且跨平台的图像解码库

    该图像解码库仅仅三个文件。 图像处理封装: spot.cpp spot.h 解码库实现: spot.c  支持图片文件格式如下: File formatRe...

    cpuimage
  • 项目中设计数据库是否要使用外键?

    学过数据库的同学都知道外键,外键能够保证数据的一致性。比如一个学生属于一个班级,班级和学生的关系是一对多,如果你删除了一个班级,那么这个班级中的学生肯定得跟着删...

    秃头哥编程

扫码关注云+社区

领取腾讯云代金券