10、trait的动态混入 及 trait的叠加

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,这个字段是直接加入到实例中的,而并非是继承来的!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,662评论 1 32
  • 这篇讲义只讲scala的简单使用,目的是使各位新来的同事能够首先看懂程序,因为 scala 有的语法对于之前使用习...
    MrRobot阅读 3,039评论 0 10
  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 6,335评论 0 10
  • 面向对象编程之类 定义一个简单的类 // 定义类,包含field以及方法 // 创建类的对象,并调用其方法 get...
    义焃阅读 848评论 0 2
  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 3,144评论 2 9

友情链接更多精彩内容