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

Spark基础-scala学习(三、Trait)的更多相关文章

  1. Spark基础-scala学习(二、面向对象)

    面向对象编程之类 //定义一个简单的类 scala> :paste // Entering paste mode (ctrl-D to finish) //类默认public的 class He ...

  2. Spark基础-scala学习(五、集合)

    集合 scala的集合体系结构 List LinkedList Set 集合的函数式编程 函数式编程综合案例:统计多个文本内的单词总数 scala的集合体系结构 scala中的集合体系主要包括:Ite ...

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

    Scala解析器的使用 REPL:Read(取值)-> Evaluation(求值)-> Print(打印)->Loop(循环).scala解析器也被称为REPL,会快速编译scal ...

  4. Spark基础-scala学习(八、隐式转换与隐式参数)

    大纲 隐式转换 使用隐式转换加强现有类型 导入隐式转换函数 隐式转换的发生时机 隐式参数 隐式转换 要实现隐式转换,只要程序可见的范围内定义隐式转换函数即可.Scala会自动使用隐式转换函数.隐式转换 ...

  5. Spark基础-scala学习(七、类型参数)

    类型参数是什么 类似于java泛型,泛型类 泛型函数 上边界Bounds 下边界 View Bounds Context Bounds Manifest Context Bounds 协变和逆变 Ex ...

  6. Spark基础-scala学习(四、函数式编程)

    函数式编程 将函数赋值给变量 匿名函数 高阶函数 高级函数的类型推断 scala的常用高阶函数 闭包 sam转换 currying函数 return 将函数赋值给变量 scala中的函数是一等公民,可 ...

  7. Spark之Scala学习

    1. Scala集合学习: http://blog.csdn.net/lyrebing/article/details/20362227 2. scala实现kmeans算法 http://www.t ...

  8. Scala学习(三)----数组相关操作

    数组相关操作 摘要: 本篇主要学习如何在Scala中操作数组.Java和C++程序员通常会选用数组或近似的结构(比如数组列表或向量)来收集一组元素.在Scala中,我们的选择更多,不过现在我们先假定不 ...

  9. Scala学习三——数组相关操作

    一.若长度固定则使用Array,若长度可能有变化则使用ArrayBuffer 固定长度数组: 如val nums=new Array[Int](10) //10个整型数组,所有元素初始化为0; val ...

随机推荐

  1. gitlab 误关闭sign-in

    sudo gitlab-rails console ApplicationSetting.last.update_attributes(password_authentication_enabled_ ...

  2. char对比varchar

    char对比varchar 相同点:char与varchar都是存储字符串的数据类型 不同点:char是固定长度的字符类型,而varchar是可变长度的字符类型,这个一定要注意.另外进行select时 ...

  3. mac电脑Git提交代码到Github提示git-credential-osxkeychain 验证解决方案

    ## 啊哈哈 这个简单,直接给出当前mac电脑登录账号密码即可,^_*,拿走不谢!!

  4. C# WPF 父控件通过使用可视化树找到子控件

    在我们使用WPF设计前台界面时,经常会重写数据模板,或者把控件放到数据模板里.但是一旦将控件放到数据模板中,在后台就没有办法通过控件的名字来获取它了,更没办法对它进行操作(例如,隐藏,改变控件的某个值 ...

  5. chrome gps位置模拟设置

    chrome gps位置模拟设置 调试公众号页面定位,Edge 虽好实现方便,介于界面实在不符合我的调试习惯  遂上度娘寻觅chrome模拟GPS方法 找了好几个帖子,发现新版本已经不再试用.不得感叹 ...

  6. xbee3的先进性功能用法

    xbee3以及xbee3 PRO 是digi无线模块的又一大突破:不仅实现了所有2.4G的模块整合,更在以后的程序更新中会增加蓝牙功能:它打通了xbee系列1和系列2之间的壁垒:不同于xbee S2C ...

  7. 浅谈React数据流管理

    引言:为什么数据流管理如此重要?react的核心思想就是:UI=render(data),data就是我们说的数据流,render是react提供的纯函数,所以用户界面的展示完全取决于数据层.这篇文章 ...

  8. Shiro与Spring整合

    Shiro引入Spring 添加jar包/maven配置 <!-- shiro支持 --> <dependency> <groupId>org.apache.shi ...

  9. 2019.03.26 bzoj4448: [Scoi2015]情报传递(归并排序+树链剖分)

    传送门 题意简述: 给一棵nnn个点的树,树上每个点表示一个情报员,一共有mmm天,每天会派发以下两种任务中的一个任务: 1.搜集情报:指派T号情报员搜集情报 2.传递情报:将一条情报从X号情报员传递 ...

  10. LOJ-10092(最大半连通子图)

    题目连通:传送门 思路: 题目定义很清晰,然后就不会了QAQ…… 后来看了书,先缩点,然后再用拓扑排序找到最长的链子的节点数(因为缩点后所有点都是一个强连通分量,所以找最长的链子就是最大限度包含 点的 ...