(Scala 11) 特质构造顺序


/**
  * 第11章 特质构造顺序
  */

import java.io.PrintWriter
//########################### 11.1 不允许多重继承 #################################
/**
  * Scala和Java都不允许多重继承,多继承会出现菱形继承问题,Scala提供了特质,
  * 类似于Java中的接口,特质可以同时拥有抽象方法和具体方法,一个类可以实现多个特质
  */

//########################### 11.2 当做接口使用的特质 #################################
trait Logger {
  def log(msg: String) //An abstract method
}

// use extends,not implements,use with to add multiple traits,all Java'Interface can be used by Scala's Trait
class ConsoleLogger extends Logger with Cloneable with Serializable with Comparable[String] {
  //No override needed
  def log(msg: String): Unit = {
    println(msg)
  }

  override def compareTo(o: String) = ???
}


//########################### 11.3 带有具体实现的特质 #################################
/**
  * 特质中的方法不一定是抽象的
  */
trait ConsoleLogger2 {
  def log(msg: String) {
    println(msg)
  }
}

class Account {
  protected var balance = 0.0
}

class SavingsAccount extends Account with ConsoleLogger2 {
  def withdraw(amount: Double): Unit = {
    if (amount > balance) log("Insufficient funds")
    else balance -= amount
  }
}

val sa = new SavingsAccount
sa.log("aa") //SavingAccount从特质ConsoleLogger2中得到一个具体的log方法(minix)混入,并不是通过继承得来的


//########################### 11.4 带有特质的对象 #################################

abstract class SavingAccount4 extends Account with Logger {
}

//匿名子类
val sa4 = new SavingAccount4 {
  override def log(msg: String) = println(msg)
}
//构建对象时,混入某个具体的特质,覆盖掉抽象方法,提供具体实现
val sa42 = new SavingAccount4 with ConsoleLogger2

//########################### 11.5 叠加在一起的特质 #################################

trait Logger5 {
  def log(msg: String)
}

trait ConsoleLogger5 extends Logger5 {
  def log(msg: String): Unit = {
    println(msg)
  }
}

//注意super
trait TimestampLogger5 extends ConsoleLogger5 {
  override def log(msg: String): Unit = {
    super.log(new java.util.Date() + " " + msg)
  }
}

trait ShortLogger5 extends ConsoleLogger5 {
  override def log(msg: String) {
    super.log(
      if (msg.length <= 15) msg else s"${msg.substring(0, 12)}..."
    )
  }
}

class Account5 {
  protected var balance = 0.0
}

abstract class SavingsAccount5 extends Account5 with Logger5 {
  def withdraw(amount: Double): Unit = {
    if (amount > balance) log("Insufficeient funds")
    else balance -= amount
  }

  //More methods...
}

/**
  * super并不是指继承关系,而是指加载顺序,继承多个相同父特质的类,会从右到左一次调用特质的方法
  * 从源码是无法判断super.method会指向哪里的方法,如果想要指定调用具体特质的方法,可以指定:super[ConsoleLogger].log(...)
  * 如果一个抽象类继承了一个特质,那么这个抽象类的子类就可以with那个特质的子特质,多个自特质是从右往左调用,super就是指向左边的特质
  */
//如acct1,先调用最右边ShortLogger5截取,再指向左边调用TimestampLogger5加时间戳,左边没了,就指向父类,调用父类的ConsoleLogger5的log方法最终输出日志
val acct1 = new SavingsAccount5 with TimestampLogger5 with ShortLogger5 //先截取字符串长度再加时间戳
val acct2 = new SavingsAccount5 with ShortLogger5 with TimestampLogger5 //先加时间戳,再截取字符串

acct1.withdraw(100) //Fri May 17 14:06:31 CST 2019 sfajosijfosa...
acct2.withdraw(100) //Fri May 17 1...


//########################### 11.6 在特质中重写抽象方法 #################################

trait Logger6 {
  def log(msg: String)
}

//提供了log方法的具体实现
trait ConsoleLogger6 extends Logger6 {
  def log(msg: String): Unit = {
    println(msg)
  }
}

//因为有super,而且方法混资Logger6.log也是个抽象方法,所以此处的log还是一个抽象方法,需要加abstract
trait TimestampLogger6 extends Logger6 {
  abstract override def log(msg: String): Unit = {
    super.log(new java.util.Date() + " " + msg)
  }
}

trait ShortLogger6 extends Logger6 {
  abstract override def log(msg: String) {
    super.log(
      if (msg.length <= 15) msg else s"${msg.substring(0, 12)}..."
    )
  }
}

class Account6 {
  protected var balance = 0.0
}

abstract class SavingsAccount6 extends Account6 with Logger6 {
  def withdraw(amount: Double): Unit = {
    if (amount > balance) log("Insufficeient funds")
    else balance -= amount
  }

  //More methods...
}

//从右往左调用log方法,直到碰到ConsoleLogger6中log实现的时候进行具体输出
val acct6 = new SavingsAccount6 with ConsoleLogger6 with TimestampLogger6 with ShortLogger6
acct6.withdraw(100)

//Fri May 17 15:43:29 CST 2019 Insufficeien...

//########################### 11.7 当做富接口使用的特质 #################################

/**
  * 富接口:有实现方法的抽象方法
  */
trait Logger7 {
  def log(msg: String)

  def info(msg: String) {
    log("INFO: ") + msg
  }

  def warn(msg: String) {
    log("WARN: ") + msg
  }

  def severe(msg: String) {
    log("SEVERE: ") + msg
  }
}

trait ConsoleLogger7 extends Logger7 {
  def log(msg: String) {
    println(msg)
  }
}

class Account7 {
  protected var balance = 0.0
}

abstract class SavingsAccount7 extends Account7 with Logger7 {
  def withdraw(amount: Double): Unit = {
    if (amount > balance) log("Insufficeient funds")
    else balance -= amount
  }

  //More methods...
}

val acct7 = new SavingsAccount7 with ConsoleLogger7
acct7.withdraw(100)


//########################### 11.8 特制中的具体字段 #################################
/**
  * 特质中可以定义具体字段,如果初始化了就是具体字段,如果不初始化就是抽象字段
  * 混入该特质的类就具有了该字段,字段不是继承,而是简单的加入类,是自己的字段
  */


trait Logger8 {
  def log(msg: String)
}

trait ConsoleLogger8 extends Logger8 {
  def log(msg: String) = {
    println(msg)
  }
}


trait ShortLogger8 extends Logger8 {
  val maxLength = 15

  abstract override def log(msg: String) {
    super.log(
      if (msg.length <= 15) msg else s"${msg.substring(0, 12)}..."
    )
  }
}

class Account8 {
  protected var balance = 0.0
}

/**
  * balance 超类字段
  * interest 子类字段
  * maxLength 子类字段
  */
class SavingsAccount8 extends Account8 with ConsoleLogger8 with ShortLogger8 {
  var interest = 0.0

  def withdraw(amount: Double) = {
    if (amount > balance) log("Insufficeient funds")
    else balance -= amount
  }

  //More methods...
}

val acct8 = new SavingsAccount8
acct8.withdraw(100)
acct8.maxLength
acct8.interest

//########################### 11.9 特质中的抽象字段 #################################
/**
  * 特质中未被初始化的字段(抽象字段)在具体的子类中必须被重写
  */

trait Logger9 {
  def log(msg: String) = println(msg)

  val maxLength: Int
}

val log9 = new Logger9 {
  override val maxLength: Int = 10
}
log9.maxLength


//########################### 11.10 特质构造顺序 #################################
/**
  * 特质也有构造器,由字段的初始化和其它特制体重的语句构成
  */
trait FileLogger extends Logger {

  println("Constructing FileLogger")

  val out = new PrintWriter("f:/app.log") //part of the trait's constructor

  out.print(s"#${java.time.Instant.now()}") //also part of the constructor

  def log(msg: String): Unit = {
    out.print(msg)
    out.flush()
  }

}

//这些特质语句在任何混入该特质的对象构造是都会被执行

class AAAFileLogger extends FileLogger {
  println("Constructing AAAFileLogger")

  def aaaLog(msg: String) = out.print(msg)
}

val aFL = new AAAFileLogger
aFL.out.close() //关闭流才会将字符串刷新至文件中

 

 

//########################### 11.11 初始化特质中的字段 #################################
/**
  * 特质不能有构造器参数,每个特质都有一个无参数的构造器
  * 缺少构造器参数是特质与类之间的唯一差别.
  * 除此之外,特质可以具备类的所有特性,比如具体和抽象的字段,以及超类
  */

trait FileLogger2 extends Logger {

  val filename: String
  val out = new PrintWriter(filename) //part of the trait's constructor

  out.print(s"#${java.time.Instant.now()}") //also part of the constructor

  def log(msg: String): Unit = {
    out.print(msg)
    out.flush()
  }

}

class Aaa {

}

//运行报空指针异常,因为这样不会在超类构造器运行前对filename进行赋值
/*val acc11 = new Aaa with FileLogger2 {
  val filename = "f:/app2.log"
}*/

//通过提前定义来实现对特质字段的赋值
val acc111 = new {
  val filename = "f:/app2.log"
} with Aaa with FileLogger2
acc111.out.close()


//如果需要在类中进行提前定义,语法如下:
class Bbb extends {
  //extends后是提前定义块
  val filename = "f:app3.log"
} with FileLogger2 {

}

val bbb = new Bbb
bbb.out.close()


//########################### 11.12 扩展类的特质 #################################

/**
  * 特质和特质的继承层次比价常见,然而特质a也可以扩展类b(继承类),这个类(b)将成为混入该特质(a)的类(c)的超类
  */
trait LoggedException extends Exception {
  def log(): Unit = {
    getMessage()
  }
}

/**
  * 类UnhappyException扩展了特质LoggedException,因为特质LoggedException继承了类Exception
  * 所以这里就相当于UnhappyExcetion继承了类Exception,并扩展了特质LoggedException
  */

class UnhappyExcetion extends LoggedException {
  override def getMessage: String = "arggh!"
}

/**
  * scala只支持类的单继承,如果现在类UnhappyException已经继承了其它类,还能扩展特质LoggedException吗?
  * 如果它继承的类是Exception子类,那可以,否则不可以
  */
class UnhappyExcetion2 extends IndexOutOfBoundsException with LoggedException

//class UnhappyExcetion3 extends Aaa with LoggedException//报错


//########################### 11.13 自身类型 #################################
/**
  * 特质除了扩展类还可以通过添加自身类型来达到相同的效果
  * 通过this:类型 => 来定义自身类型,在特质的方法中,可以调用该自身类型的任何方法
  */
trait LoggedException2 {
  this: Exception =>
  def log(): Unit = {
    getMessage()
  }
}

/**
  * 它要求所有使用该特质的类必须是制定类型的子类
  */
class UnhappyExcetion3 extends IndexOutOfBoundsException with LoggedException2 {
  override def getMessage() = "asdf~"
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值