scala009_面向对象编程之Trait

1 trait基础知识

1.1 将trait作为接口使用(仅仅定义抽象方法)

1.2 在trait中定义具体方法

1.3 在trait中定义具体字段

1.4 在trait中定义抽象字段

2 trait高级知识

2.1 为实例对象混入trait

在创建对象的时候动态的继承不同的trait,不同的对象继承不同的trait,也就会有不同的行为和特点

2.2 trait调用链

一个类继承多个trait,可以一次调用多个trait的实现的同一个方法。设计模式:责任链(多个相似的对象,以此调用下一个对象同一个方法,组成调用链条)

2.3 在trait中覆盖抽象方法

trait可以继承其他trait ,子trait可以覆盖父trait的抽象方法,给抽象方法的具体实现。

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

2.5 trait的构造机制

2.6 trait字段的初始化

2.7 让trait继承类

trait既可以继承trait,也可以继承类

1 将trait作为接口使用

scala中的trait是一种特殊的概念
首先我们可以将trait作为接口来使用,此时的trait就与java中的接口非常类似
在trait中可以定义抽象方法,就与抽象类中的方法一样,只要不给出方法的具体实现即可
类可以使用extends关键字继承trait,注意,这里不是implement,而是extends,在scala中没有implement的概念,无论继承类还是trait,统一都是extends
类继承trait后,必须实现其中的抽象方法,实现时不需要使用override关键字
scala不支持对类进行多继承,但是支持多重继承trait,使用with关键字即可

scala> :paste
// Entering paste mode (ctrl-D to finish)

trait SayHello{
 def sayhello(name : String)
}
trait MakeFriends{
 def makefriends(p : Person)
}
class Person(val name:String) extends SayHello with MakeFriends{
 def sayhello(othername :String) = print("Hello " + othername)
 def makefriends(p:Person) = println("Hello  I'm " + p.name + ",I'm "+name+"I want to make firends with you.")
}

// Exiting paste mode, now interpreting.

defined trait SayHello
defined trait MakeFriends
defined class Person

scala> val p = new Person("zhu")
p: Person = Person@6145b81e

scala> val p1 = new Person("de")
p1: Person = Person@4dba773d

scala> p.sayhello("jack")
Hello jack

scala> p.makefriends(p1)
Hello  I'm de,I'm zhuI want to make firends with you.


2 在Trait中定义具体方法

scala中的trait可以不是只定义抽象方法,还可以定义具体方法,此时trait更像是包含了通用工具方法的东西。
有一个专有的名词来形容这种情况,就是说trait的功能混入了类
举例来说,trait中可以包含一些很多类都通用的功能方法,比如打印日志等等,spark中就是用trait来定义了通用的日志打印方法。

scala> :paste
// Entering paste mode (ctrl-D to finish)

trait Logger{
 def log(msg:String) = println("log: " + msg)
}
class Person(val name:String) extends Logger{
 def sayhello = {println("Hello,I'm " + name);log("sayhello is invoked")}
}

// Exiting paste mode, now interpreting.

defined trait Logger
defined class Person

scala> val p  = new Person("jack")
p: Person = Person@7cff3f1d

scala> p.sayhello
Hello,I'm jack
log: sayhello is invoked

scala>

3 在Trait中定义具体字段

scala中的trait可以定义具体field,此时继承trait的类就自动获取了trait中定义的field
但是这种获取trait的方式与继承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("Hello,I'm " + name + " I have " + eyeNum  + " eyes.")
}

// Exiting paste mode, now interpreting.

defined trait Person
defined class Student

scala> val s = new Student("jack")
s: Student = Student@250967f1

scala> s.sayhello
Hello,I'm jack I have 2 eyes.

scala>

4 在Trait中定义抽象字段

scala中Trait可以定义抽象field,而trait中的具体方法则可以基于抽象field来编写
但是继承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)
  println("I'm " + name + " , I want to make friends wit you!")
 }
}

// Exiting paste mode, now interpreting.

defined trait SayHello
defined class Person

scala> val p = new Person("leo")
p: Person = Person@559fd5ec

scala> val p1 = new Person("jack")
p1: Person = Person@35ceec81

scala> p.msg
res21: String = hello

scala> p.makeFriends(p1)
hello , jack
I'm leo , I want to make friends wit you!

scala>

5 为实例混入trait

有时候我们可以在创建类的对象时,指定该对象混入某个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("jack")
p1: Person = Person@548f4f60

scala> p1.sayHello
Hi,I'm jack

scala> val p2 = new Person("leo") with MyLogger
p2: Person with MyLogger = $anon$1@2c9af700

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

scala>

6 trait调用链

scala中支持让类继承多个trait后,依次调用多个trait中的同一个方法,只要让多个trait的同一个方法中,在最后都执行super方法即可。
类中调用多个trait中都有的这个方法时,首先胡从最右边的trait方法开始执行,然后一次往左执行,形成一个调用链条
这种特性非常强大,其实就相当于设计模式中的责任链模式的一种具体实现依赖

scala> :paste
// Entering paste mode (ctrl-D to finish)

trait Handler{
 def handler(data:String){}
}
trait DataValidHandler extends Handler{
 override def handler(data:String){
  println("Check Data "+data)
  super.handler(data)
 }
}
trait SignatureValidHandler extends Handler{
 override def handler(data:String){
  println("Check Signature "+data)
  super.handler(data)
 }
}
class Person(val name:String) extends SignatureValidHandler with DataValidHandler{
 def sayhello = {print("Hello "+name);handler(name)}
}

// Exiting paste mode, now interpreting.

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

scala> val p = new Person("jack")
p: Person = Person@265adfad

scala> p.sayhello
Hello jackCheck Data jack
Check Signature jack

scala>

7 在trait中覆盖抽象方法

在trait中,是可以覆盖父trait的抽象方法的
但是覆盖时,如果使用了super.方法的代码,则无法通过编译。因为super.f方法就回去调用父trait的抽象方法,此时子trait的该方法还是会被认为是抽象的
此时如果要通过编译,就得给子trait的方法加上abstract override修饰

scala> :paste
// Entering paste mode (ctrl-D to finish)

trait Logger{
 def log(msg:String)
}
trait Mylogger extends Logger{
 abstract override def log(msg:String){super.log(msg)}
}

// Exiting paste mode, now interpreting.

defined trait Logger
defined trait Mylogger

scala>

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

在trait中,可以混合使用具体方法和抽象方法
可以让具体方法依赖于抽象方法,而抽象方法则放到继承trait的类中去实现
这种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@236ab296

scala> val p = new Person("jack")
false
p: Person = Person@28b576a9

scala>

9 trait的构造机制

trait与class的区别,trait不能定义接收参数的构造函数

在scala中,trait也是有构造代码的,也就是trait中的,不包含在任何方法中的代码
而继承了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("Persons's constructor!")}
trait Logger{println("Logger's constructor!")}
trait MyLogger extends Logger{println("MyLogger's constructor!")}
trait TimeLogger extends Logger {println("TimeLogger's constructor!")}
class Student extends Person with MyLogger with TimeLogger{
 println("Student's 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()
Persons's constructor!
Logger's constructor!
MyLogger's constructor!
TimeLogger's constructor!
Student's constructor!
s: Student = Student@4287d447

scala>
scala> :paste
// Entering paste mode (ctrl-D to finish)

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

// Exiting paste mode, now interpreting.

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

scala> val s = new Student
Person
Logger
MyLogger
TimeLogger
LoggerSecond
SysLogger
Student
s: Student = Student@7b22ec89

scala>

10 trait field的初始化

在scala中,trait是没有接收参数的构造函数,这是trait与class的唯一区别,但是如果需求就是要trait能够对field进行初始化。该怎么办呢?只能使用scala中非常特殊的一种高级特性-----提前定义

scala> trait SayHello{
     |  val msg:String
     |  println(msg.toString)
     | }
defined trait SayHello

scala> class Person extends SayHello{
     |  val msg:String ="init"
     | }
defined class Person

scala> val p = new Person
java.lang.NullPointerException
  at SayHello$class.$init$(<console>:13)
  ... 33 elided

scala> class Person
defined class Person
#提前定义
#构造对象之前提前重写了msg,相当于初始化sayHello的那两行代码之前,msg被重写了
scala> val p = new {
     |  val msg:String = "init"
     | } with Person with SayHello
init
p: Person with SayHello = $anon$1@60e21209
# 提前定义
scala> class Person extends { val msg:String = "init"} with SayHello
defined class Person

scala> val p = new Person
init
p: Person = Person@5fad41be

scala>
scala> trait SayHello{
     | lazy val msg:String = null
     | println(msg.toString)
     | }
defined trait SayHello

scala> class Person extends SayHello
defined class Person

scala> val p = new Person
java.lang.NullPointerException
  at SayHello$class.$init$(<console>:13)
  ... 33 elided

scala>

使用lazy 提前定义

scala> trait SayHello{
     | lazy val msg:String = null
     | println(msg.toString)
     | }
defined trait SayHello

scala> class Person extends SayHello
defined class Person

scala> val p = new Person
java.lang.NullPointerException
  at SayHello$class.$init$(<console>:13)
  ... 33 elided

scala> class Person extends SayHello{
     |  override lazy val msg:String = "init"
     | }
defined class Person

scala> val p = new Person
init
p: Person = Person@5b498842

scala>

11 trait继承class

在scala中,trait也可以继承自class,此时这个class就会成为所有继承该trait的类的父类

scala> class MyUtil{
     |  def printMessage(msg:String) = println(msg)
     | }
defined class MyUtil

scala> trait Logger extends MyUtil{
     |  def log(msg:String) = printMessage("log: " + msg)
     | }
defined trait Logger

scala> class Person(val name:String) extends Logger{
     |  def sayHello{
     |   log("Hi I'm " + name)
     |   printMessage("Hi,I'm " + name)
     | }
     | }
defined class Person

scala> val p = new Person("leo")
p: Person = Person@2dd8239

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

scala>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值