1、问题引入
/**
* Created by dwh on 2019/12/20.
*/
object MyTraitMixIn {
def main(args: Array[String]): Unit = {
val person1 = new Person() // 定义一个Person对象
person1.name = "tom"
person1.sayHello()
person1.sayHi()
val student1 = new Student() // 定义一个Student对象
student1.name = "jerry"
student1.sayHello()
student1.sayHi()
student1.doHomeWork()
}
}
// 1、定义一个trait, 里面有一个抽象函数和一个非抽象函数
trait MyTrait {
def sayHello()
def sayHi(): Unit = {
println("Hi~~")
}
}
// 2、定义一个类Person, 继承了一个trait, 那么就继承了它的所有函数
class Person() extends MyTrait {
var name: String = _
override def sayHello(): Unit = {
println("Hello," + this.name)
}
}
// 3、定义一个类Student, 继承了Person类
class Student extends Person {
def doHomeWork(): Unit = {
println("do homework....")
}
}
问题引入:
- 可以看到上面的代码,Person类从MyTrait继承的函数,也被传递给了Person的子类!
- 目的:想让Person类拥有MyTrait函数,而不想让Person的子类也拥有MyTrait的函数!
- 思考:直接在Person类定义的时候就extends MyTrait 显然是做不到的,那么就只能在定义Person对象是做一些手脚了。
- 解决方案:动态混入,在不影响原有的继承关系的基础上,给制定的类扩展功能。
2、混入1个trait
- 将上面的代码修改一下:
/**
* Created by dwh on 2019/12/20.
*/
object MyTraitMixIn {
def main(args: Array[String]): Unit = {
val person1 = new Person // 定义一个Person对象
person1.name = "tom"
// person1.sayHello() //
// person1.sayHi() // 以上这两句代码会报错,因为Person类没有继承MyTrait, 也就没有了这两个函数了
val student1 = new Student // 定义一个Student对象
student1.name = "jerry"
// student1.sayHello()
// student1.sayHi() // 以上这两句也会报错, 因为Person类没有继承MyTrait, 也就没有了这两个函数了, 那么Student作为他的子类, 当然也就没有这两个函数了
student1.doHomeWork()
// 重点来了: 给Person对象动态混入MyTrait的函数
val person2 = new Person with MyTrait {
override def sayHello(): Unit = { // 这里动态混入了trait, 那么势必要实现trait里面的抽象函数, 有点儿类似于java的匿名内部类
println("Hello," + this.name)
}
}
person2.name = "spark"
person2.sayHi() // 这个会调用从MyTrait继承来的函数
person2.sayHello() // 这个是自己从MyTrait继承来又自己实现了的函数
}
}
// 1、定义一个trait, 里面有一个抽象函数和一个非抽象函数
trait MyTrait {
def sayHello()
def sayHi(): Unit = {
println("Hi~~")
}
}
// 2、定义一个类Person
class Person() {
var name: String = _
}
// 3、定义一个类Student, 继承了Person类
class Student extends Person {
def doHomeWork(): Unit = {
println("do homework....")
}
}
从上面代码可以看出,可以只给某一个对象实例混入trait。
3、混入n个特质(n个特质之间没有关联)
/**
* Created by dwh on 2019/12/20.
*/
object MyTraitMixIn {
def main(args: Array[String]): Unit = {
// 给Person对象动态混入MyTrait, MyTrait1, MyTrait2
val person2 = new Person with MyTrait with MyTrait1 with MyTrait2 {
override def sayHello(): Unit = { //
println("Hello," + this.name)
}
}
person2.name = "spark"
person2.sayHi() // 这个会调用从MyTrait继承来的函数
person2.sayHi1()
person2.sayHi2()
person2.sayHello() // 这个是自己从MyTrait继承来又自己实现了的函数
}
}
// 1、定义一个trait, 里面有一个抽象函数和一个非抽象函数
trait MyTrait {
def sayHello()
def sayHi(): Unit = {
println("Hi~~")
}
}
// 1、定义一个trait, 里面有一个抽象函数和一个非抽象函数
trait MyTrait1 {
def sayHello()
def sayHi1(): Unit = {
println("Hi1~~")
}
}
// 1、定义一个trait, 里面有一个抽象函数和一个非抽象函数
trait MyTrait2 {
def sayHello()
def sayHi2(): Unit = {
println("Hi2~~")
}
}
// 2、定义一个类Person
class Person() {
var name: String = _
}
// 3、定义一个类Student, 继承了Person类
class Student extends Person {
def doHomeWork(): Unit = {
println("do homework....")
}
}
从上面代码可以看出,可以给对象实例混入多个trait。
ps:当混入多个trait时,多个trait之间可以有相同的抽象方法,但是如果多个tarit之间有相同的非抽象方法,那么此时会报错误:anonymous class $anon inherits conflicting members
4、混入n个特质(n个特质之间有继承关系)
- 高能预警, 下面的代码有可能引起不适,请谨慎观看:
package cn.tujia.bigdata
/**
* Created by dwh on 2019/12/20.
*/
object MyTraitMixIn {
def main(args: Array[String]): Unit = {
// 给Person对象动态混入MyTrait4, MyTrait3, MyTrait2, MyTrait1, MyTrait
// 0、首先开始混入第1个trait: 即MyTrait4, 此时代码走到了MyTrait4, 直接打印"MyTrait4", 至此第1个trait混入完毕。
// 1、接着开始混入第2个trait: 即MyTrait3, 此时代码走到了MyTrait3, 发现MyTrait3还继承了MyTrait, 而MyTrait已经是最顶层了,
// 所以此时先打印"MyTrait", 再打印"MyTrait3", 至此第2个trait混入完毕。
// 2、然后开始混入第3个trait: 即MyTrait2, 此时代码走到了MyTrait2, 发现MyTrait2还继承了MyTrait1, 紧接着发现MyTrait1继承了MyTrait, 但是呢, MyTrait刚刚已经混入过了, 所以MyTrait就不会再次混入了,
// 于是就打印了"MyTrait1", 再打印"MyTrait2", 至此第3个trait混入完毕。
// 3、再然后准备混入第4个trait, 即MyTrait1, 发现已经混入过了, 不再混入
// 4、最后准备混入第5个trait, 即MyTrait, 发现也已经混入过了,不再混入, 至此所以trait混入完毕!
// 5、总结:
// 混入时要按照从左到右的顺序依次混入:
// 首先混入第1个trait, 如果这个trait有父trait, 那么会一直往上找, 直到把它所有的父类都混入
// 接着混入第2个trait, 同样如果这个trait也有父类, 那么也会一直往上找, 知道把它所有的父类都混入,
// 然后混入第3个trait, 第4个trait, ... 一直到第n个trait
// 需要注意的是:trait只会被混入一次, 即类只会被jvm加载一次!
val person2 = new Person with MyTrait4 with MyTrait3 with MyTrait2 with MyTrait1 with MyTrait {
override def sayHello(): Unit = {
println("Hello," + this.name)
}
}
person2.name = "spark"
// 函数的执行顺序和混入顺序刚好相反, 混入是从左到右依次混入, 而函数的执行顺序是从右到左依次执行:
// 1、调用sayHi函数, 它会先去找从右边数第一个trait,即MyTrait, 然后随即发现他有个子类是MyTrait1, 紧接着又发现MyTrait1又有子类MyTrait2, 而MyTrait2已经没有子类了
// 所以会首先调用了MyTrait2的sayHi函数, 马上就打印出"Hi2~~"
// 2、打印出"Hi2~~"后, MyTrait2的sayHi函数还未执行完, 这个函数还有一句super.sayHi, 所以紧接着就执行super.sayHi这句话:
// 3、要注意的是super.sayHi并不是指MyTrait2的父类中的函数, 而是指混入顺序在MyTrait2前的那个trait, 即MyTrait3
// 4、于是就开始调用MyTrait3的sayHi函数, 于是就打印了"Hi3~~"
// 5、然后在MyTrait3的sayHi函数也有一句super.sayHi, 那么此时应该调用混入顺序在MyTrait3前的那个trait了, 但是发现它前头已经没有trait了, 那么此时的super就是指真正的父类了,即MyTrait1,
// 6、所以紧接着就打印了"Hi1~~"
// 7、然后就又从MyTrait1.sayHi函数中的super.sayHi, 调用了父类MyTrait, 于是接着打印出了"Hi~~"
// 8、因为函数是一层一层调用进来的, 调用完还要一层一层再退回去, 所以MyTrait的sayHi执行完, 就代表着MyTrait1的sayHi函数中的super.sayHi执行完了, 然后紧接着执行打印"Hi11~~"
// 9、同理, 再往上层调用处返回, 就是MyTrait3的sayHi函数中的super.sayHi执行完了, 于是就打印了"Hi33~~"
// 10、再往回返, 就返到了MyTrait2的sayHi函数中的super.sayHi函数, 所以就又打印出了"Hi22~~"
// 11、此时调用结束。 (ps: 可以看出, person2对象调用了sayHi函数, 实际调用的是MyTrait2的sayHi函数, 从打印Hi2~~开始, 中间有各种深层次的调用, 最终又逐层返回到Hi22~~, 这个跟java的方法栈是一模一样的)
// 总结:
// 1、混入函数调用时, 是从右往左调用:
// 2、如果右边的第一个trait有子类, 那么他本身的函数就不会被调用了, 子类的函数会被调用, 如果子类还有子类, 那么依次深入, 直到最底层的那个子类的函数!
// 3、最底层的函数执行调用时, 它的函数体内可能又调用了别的函数, 然后一层一层调用, 最后再一层一层滴返回来, 直到把那个子类的函数体的语句全部运行完毕!
// 4、如果函数体内都是正常的语句, 那么这个函数执行完了就紧接着该执行他前头的那个trait的函数了,
// 5、但如果函数体内是不正常的语句, 即有super.xxx这样的语句, 需要注意的是, 这里的super指的可不是这个子类的父类, 而是混入顺序在他前头的那个trait
person2.sayHi()
person2.sayHi4() // ps: 如果trait和其他的trait没有继承关系, 那么直接正常调用即可 !
person2.sayHello()
}
}
// 1、定义一个trait, 里面有一个抽象函数和一个非抽象函数
trait MyTrait {
println("MyTrait")
def sayHello()
def sayHi(): Unit = {
println("Hi~~")
}
}
// 2、定义一个trait, 里面有一个抽象函数和一个非抽象函数
trait MyTrait1 extends MyTrait {
println("MyTrait1")
def sayHello()
override def sayHi(): Unit = {
println("Hi1~~")
super.sayHi()
println("Hi11~~")
}
}
// 3、定义一个trait, 里面有一个抽象函数和一个非抽象函数
trait MyTrait2 extends MyTrait1 {
println("MyTrait2")
def sayHello()
override def sayHi(): Unit = {
println("Hi2~~")
super.sayHi()
println("Hi22~~")
}
}
// 4、定义一个trait, 里面有一个抽象函数和一个非抽象函数
trait MyTrait3 extends MyTrait {
println("MyTrait3")
def sayHello()
override def sayHi(): Unit = {
println("Hi3~~")
super.sayHi()
println("Hi33~~")
}
}
// 4、定义一个trait, 里面有一个抽象函数和一个非抽象函数
trait MyTrait4 {
println("MyTrait4")
def sayHello()
def sayHi4(): Unit = {
println("Hi4~~")
}
}
// 5、定义一个类Person
class Person() {
var name: String = _
}
// 6、定义一个类Student, 继承了Person类
class Student extends Person {
def doHomeWork(): Unit = {
println("do homework....")
}
}
一个注意点:
如果trait中既有抽象函数又有非抽象函数,那么这个trait也称为富接口。
如果trait中有非抽象字段,那么当某个实例混入该trait,这个字段是直接加入到实例中的,而并非是继承来的!