接口概念(Java回顾)

在 Java 中有接口的概念
定义接口:interface 接口名
实现接口:class 类名 implements 接口名1, 接口名2
使用小结:

  • 一个类可以实现多个接口
  • 接口之间支持多继承
  • 接口中属性都是常量
  • 接口中方法都是抽象的

特质介绍

在 Scala 中,当有多个类有相同的特征时,可以将这些特征(特质)独立出来,采用关键字 trait 声明
作用:代替 Java 的接口;是对 Scala 单继承机制的一种补充
理解:特质相当于 Java 中的 interface + abstract class
注意:特质名一般首字母大写**

  1. // 语法
  2. trait 特质名 {
  3. // trait体
  4. }
  5. // 使用
  6. // 方式一:没有父类
  7. class 类名 extends Trait1 with Trait2 ...
  8. // 方法二:有父类
  9. class 类名 extends 父类 with Trait1 with Trait2 ...

案例研究

  1. // 自行测试
  2. object Test {
  3. def main(args: Array[String]): Unit = {
  4. val b = new B
  5. b.getConnection()
  6. println(b.getUser(100))
  7. }
  8. }
  9. // 定义一个特质
  10. trait Driver {
  11. // 抽象方法
  12. def getConnection()
  13. // 具体方法(即非抽象方法)
  14. def getUser(id: Int): String = s"得到 id 为 ${id} 的用户数据"
  15. }
  16. class A {}
  17. class B extends A with Driver {
  18. override def getConnection(): Unit = println("连接MySQL数据库")
  19. }

📝 说明

  • 特质可以包含抽象方法和非抽象方法;这两种方法都有的特质称为富接口
  • 一个类通过 extends 继承一个特质或父类,通过 with 关键字继承多个特质
  • 所有的 Java 接口都可以当做 Scala 的特质使用

    动态混入

    使用特质有两种方式:
  1. 在声明类时继承特质
  2. 在创建对象时混入特质(即动态混入,这是 Scala 特有的方式)

优点:可以在不修改类定义的情况下,扩展目标类的功能,非常灵活,耦合性低
注意:抽象类动态混入特质时还需要通过创建匿名子类对象的方式实现抽象方法

  1. object TraitDemo {
  2. def main(args: Array[String]): Unit = {
  3. // 类混入特质
  4. val mysql = new Mysql with Driver
  5. mysql.insert("mysql", 99)
  6. // 抽象类混入特质(还需要实现抽象方法)
  7. val oracle = new Oracle with Driver {
  8. override def say(): Unit = println("这个数据库是Oracle")
  9. }
  10. oracle.insert("oracle", 11)
  11. oracle.say()
  12. val mongodb = new Mongodb
  13. mongodb.insert("mongodb", 22)
  14. }
  15. }
  16. trait Driver {
  17. def insert(db: String, id: Int): Unit = {
  18. println(db + " :插入数据的id为 " + id)
  19. }
  20. }
  21. class Mysql {}
  22. abstract class Oracle {
  23. def say()
  24. }
  25. // 一个类直接继承一个特质
  26. class Mongodb extends Driver {}

叠加特质

定义:创建对象的同时如果混入多个特质,称为叠加特质
顺序:特质声明顺序从左往右,构建顺序从左往右,方法执行顺序从右往左⭐

  1. // 自行测试
  2. object Test {
  3. def main(args: Array[String]): Unit = {
  4. val p = new Person with Trait3 with Trait4
  5. p.showInfo(123)
  6. }
  7. }
  8. trait Trait1 {
  9. println("Trait1 ...")
  10. def showInfo(id: Int)
  11. }
  12. trait Trait2 extends Trait1 {
  13. println("Trait2 ...")
  14. // 重写/实现抽象方法
  15. def showInfo(id: Int): Unit = {
  16. println("Trait2的id:" + id)
  17. }
  18. }
  19. trait Trait3 extends Trait2 {
  20. println("Trait3 ...")
  21. // 重写方法
  22. override def showInfo(id: Int): Unit = {
  23. println("Trait3 调用 Trait2 的方法")
  24. super.showInfo(id + 2)
  25. }
  26. }
  27. trait Trait4 extends Trait2 {
  28. println("Trait4 ...")
  29. // 重写方法
  30. override def showInfo(id: Int): Unit = {
  31. println("Trait4 调用 Trait2 的方法")
  32. super.showInfo(id)
  33. super[Trait2].showInfo(id)
  34. }
  35. }
  36. class Person {
  37. println("Person ...")
  38. }

📝 执行结果

  1. // 构建顺序
  2. Person ...
  3. Trait1 ...
  4. Trait2 ...
  5. Trait3 ...
  6. Trait4 ...
  7. // 执行顺序
  8. Trait4 调用 Trait2 的方法
  9. Trait3 调用 Trait2 的方法
  10. Trait2id125
  11. Trait2id123

📝 详细说明

  • 动态混入时,如果特质中使用了 super 关键字,并不是调用了父特质的方法,而是向左边继续查找特质,找不到才会去父特质查找
  • 如果想要调用具体方法的特质,可以指定:super[特质].xxx()【注意泛型必须是该特质的直接超类类型】

    ✍ 思考题

    目前为止,在 Scala 中创建对象共有几种方式?
  1. 使用 new 关键字创建
  2. 通过 apply 方式创建
  3. 使用匿名子类创建
  4. 使用动态混入创建