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>