Scala面向对象之特质语法

  • 所有的面向对象的语言都不允许直接的多重继承,因为会出现“deadly diamond of death”问题。 Scala 提供了特质(trait) ,特质可以同时拥有抽象方法和具体方法,一个类可以实现多个特质。
  • 特质中没有实现的方法就是抽象方法。类通过 extends 继承特质,通过 with 可以继承多个特质。
  • 特质的方法可以抽象也可以不抽象。
object Scala05_Trait01 {
  def main(args: Array[String]): Unit = {
    val log:Logger = new Console
    log.log("Println MSG ...")
    log.printLog()
  }
}

/**
 * 特质更像是Java中的抽象类
 */
trait Logger {
  def log(msg: String)

  def printLog():Unit={
    println("Println Log。。。")
  }
}
// 所有的 java 接口都可以当做 Scala 特质使用
class Console extends Logger with Cloneable with Serializable {
  def log(msg: String) {
    println(msg)
  }
}

运行结果:

Println MSG ...
Println Log。。。

1 带有具体实现的特质

  • 和 Java 中的接口不太一样的是特质中的方法并不一定是抽象的,也可以有默认实现。
object Scala01_Trait {
  def main(args: Array[String]): Unit = {
    val stu:Person001 = new Student001
    stu.speak()
    stu.walk()
  }
}

trait Person001{
  //具体的方法:
  def speak()={
    println("说话")
  }

  //抽象的方法
  def walk()
}

class Student001 extends Person001{
  def walk():Unit = {
    println("六亲不认的步伐")
  }
}
说话
六亲不认的步伐

2 带有特质的对象,动态混入

  • Java的借口可以作为特质混入
  • 可以混入多个特质
object Scala02_Trait {
  def main(args: Array[String]): Unit = {
    //动态混入特质
    val mysqlDB = new MySQLDB001 with DB001
    mysqlDB.insert(21)
    mysqlDB.delete(10)
  }
}

trait DB001{
  def insert(id:Int)={
    println("插入数据id=" + id)
  }
}

class MySQLDB001{
  def delete(id:Int)={
    println("删除数据id=" + id)
  }
}
插入数据id=21
删除数据id=10

3 叠加在一起的特质

  • 构建对象的同时如果混入多个特质, 那么特质声明顺序从左到右,方法执行顺序从右到左
object Scala03_Trait {
  def main(args: Array[String]): Unit = {
    // 叠加在一起的特质叠加顺序是从右向左再向上
    // 叠加在一起的特质加载顺序是从左到右的
    var mysql = new MySQL002 with Data002 with File002 with DB002
    mysql.insert(2)
  }
}

trait Operate002 {
  println("Oper002...")
  def insert( id : Int )
}
trait Data002 extends Operate002 {
  println("Data002...")
  def insert( id : Int ) {
    println("插入数据【"+id+ "】 " )
  }
}
trait File002 extends Data002 {
  println("File002...")
  override def insert( id : Int ) {
    print("向文件" )
    super.insert(id)
  }
}
trait DB002 extends Data002 {
  println("DB002...")
  override def insert( id : Int ) {
    print("向数据库" )
    super.insert(id)
  }
}

class DataBase002{
  println("DataBase002.。。")
}
class MySQL002 extends DataBase002{
}
DataBase002.。。
Oper002...
Data002...
File002...
DB002...
向数据库向文件插入数据【2
  • 此时会发现 super 关键字并不是我们理解的调用父特质的方法,而是调用下一个特质的同名方法(如果存在的话)
  • 如果想要调用具体特质的方法,可以指定: super[Data].xxx(…).其中的泛型必须是该特质的直接超类类型

4 在特质中重写抽象方法

  • 如果调用父特质的抽象方法,为了保证一定要有方法实现,可以让当前特质重写父特质中的抽象方法。 防止混入特质时出现错误。
object Scala04_Trait {
  def main(args: Array[String]): Unit = {
    val mysql004 = new MySQL004 with DB004 with Data004
    mysql004.insert(10)
    /*
    加入 abstract override修饰词,下面这种语法不再被编译通过了
    val mysql04 = new MySQL004 with Data004
     */

  }
}

trait Operate004 {
  //抽象insert方法
  def insert( id : Int )
}
trait Data004 extends Operate004 {
  // abstract override def insert( id : Int )
  override abstract def insert( id : Int ) {
    super.insert(id) // 调用父特质的抽象方法,那么在实际使用时,没有方法的具
    //体实现,无法编译通过,为了避免这种情况的发生。可重写抽象方法,这样在使用时,
    //就必须实现该方法,就不会出现错误了,参考特质 DB
  }
}
trait DB004 extends Operate004 {
  def insert( id : Int ) {
    println("id =" + id)
  }
}

class MySQL004{

}
id =10

在这里插入图片描述

5 当作富接口使用的特质

  • 即该特质中既有抽象方法,又有非抽象方法

6 特质中的具体字段

  • 特质中可以定义具体字段,如果初始化了就是具体字段,如果不初始化就是抽象字段。
  • 混入该特质的类就具有了该字段,字段不是继承,而是简单的加入类。是自己的字段。
  • Java中的接口中的字段只能是公开静态常量。
trait FieldDemo{
  var name = "zhangfei"
  var age:Int

  def testName

  def printlnName={
    println(name)
  }
}

class FieldImplDemo extends FieldDemo{
  override var age: Int = _

  override def testName: Unit = ???
}

7 特质中的抽象字段

  • 特质中未被初始化的字段在具体的子类中必须被重写。

8 特质构造顺序

  • 特质也是有构造器的,构造器中的内容由“字段的初始化”和一些其他语句构成。具体实现请参考“叠加在一起的特质”
    步骤总结:
    1)、调用当前类的超类构造器
    2)、第一个特质的父特质构造器
    3)、第一个特质构造器
    4)、第二个特质构造器的父特质构造器, 如果已经执行过,就不再执行
    5)、第二个特质构造器
    6)、当前类构造器

9 初始化特质中的字段

  • 在某些情况下, Scala 特质和 Java 抽象类是非常类似的,所以也有可能会在声明属性时,出现属性值发生改变的情况,问题请参考“构造顺序和提起定义”

10 扩展类的特质

10.1 特质可以继承自类,以用来拓展该类的一些功能

trait LoggedException extends Exception{
  def log(): Unit ={
    println(getMessage()) // 方法来自于 Exception 类
  }
}

10.2 所有混入该特质的类,会自动成为那个特质所继承的超类的子类

class UnhappyException extends LoggedException{
override def getMessage = "错误消息! "// 已经是 Exception 的子类了,所以可以重写方法
}

10.3 如果混入该特质的类,已经继承了另一个类,不就矛盾了? 注意,只要继承的那个类是特质超类的子类即可。

object Scala05_Trait {
  def main(args: Array[String]): Unit = {

    val log = new UnhappyException
    log.log()
  }
}

trait LoggedException extends Exception{
  def log(): Unit ={
    println(getMessage()) // 方法来自于 Exception 类
  }
}

class MySelfClass extends IndexOutOfBoundsException{

}

//保证MySelfClass和LoggedException最后有相同的父类
class UnhappyException extends MySelfClass with LoggedException{
  override def getMessage = "错误消息! "// 已经是 Exception 的子类了,所以可以重写方法
}

11 自身类型

  • 主要是为了解决特质的循环依赖问题,同时可以确保特质在不扩展某个类的情况下,依然可以做到限制混入该特质的类的类型。
//自身类型特质
trait Logger{
  this: Exception => // 明确告诉编译器,我就是 Exception
  def log(): Unit ={
    println(getMessage) // 既然我就是 Exception, 那么就可以调用其中的方法
  }
}

class MySQL extends Exception { // 此处必须继承 Exception 类,否则无法混入 logger特质
}

object Scala05_Trait {
  def main(args: Array[String]): Unit = {

//    val log = new UnhappyException
//    log.log()

    val mysql = new MySQL with Logger
    mysql.log()
  }
}
null
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值