/**
* 第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~"
}