11.Scala-特质
第11章 特质
11.1 不允许多重继承
11.2 当做接口使用的特质
trait Logger { def log(msg: String) // An abstract method }
class ConsoleLogger extends Logger with Cloneable with Serializable { // Use extends, not implements. Use with to add multiple traits def log(msg: String) {
println(msg)
} // No override needed }
11.3 带有具体实现的特质
trait ConsoleLogger { def log(msg: String) { println(msg) } } class Account { protected var balance = 0.0 } class DrawAccount extends Account with ConsoleLogger { def withdraw(amount: Double) { if (amount > balance) log("Insufficient funds") else balance -= amount } // More methods ... }
11.4 带有特质的对象,动态混入
trait Logger { def log(msg: String); }
// 继承 Logger trait ConsoleLogger extends Logger { def log(msg: String) { println(msg) } }
class Account { protected var balance = 0.0 }
abstract class SavingsAccount extends Account with Logger { def withdraw(amount: Double) { if (amount > balance) log("Insufficient funds") else balance -= amount } // More methods ... }
object Main extends App { val acct = new SavingsAccount with ConsoleLogger //动态混入 acct.withdraw(100) }
/*
* 当前 new 的这个类,没有实现这个类所继承的带有抽象方法的特质,这个时候,可以在 new 的同时,混入一个已经实现该特质抽象方法的特质。
*/
11.5 叠加在一起的特质
/** * 叠加特质的执行顺序 * 1、动态混入:从右向左执行 * 2、情景“混入的多个特质,是继承了同一个特质 * 3、super 关键字为向左调用 * 4、如果左边没有特质了,则调用父特质中的方法 */ trait Logger3 { def log(msg: String); } // 继承 Logger 特质,提供具体的 log 方法 trait ConsoleLogger3 extends Logger3 { def log(msg: String) { println(msg) } } // 注意 super trait TimestampLogger3 extends ConsoleLogger3 { override def log(msg: String) { super.log(new java.util.Date() + " " + msg) } } trait ShortLogger3 extends ConsoleLogger3 { override def log(msg: String) { super.log( if (msg.length <= 15) msg else s"${msg.substring(0, 12)}...") } } class Account3 { protected var balance = 0.0 } abstract class SavingsAccount3 extends Account3 with Logger3 { def withdraw(amount: Double) { if (amount > balance) log("Insufficient funds") else balance -= amount } // More methods ... } object Main3 extends App { val acct1 = new SavingsAccount3 with TimestampLogger3 with ShortLogger3 acct1.withdraw(100) //Thu Jul 04 00:23:24 CST 2019 Insufficient... val acct2 = new SavingsAccount3 with ShortLogger3 with TimestampLogger3 acct2.withdraw(100) //Thu Jul 04 0... }
11.6 在特质中重写抽象方法
trait Logger4 { def log(msg: String) // This method is abstract } //因为有 super,Scala 认为 log 还是一个抽象方法 trait TimestampLogger4 extends Logger4 { abstract override def log(msg: String) { super.log(new java.util.Date() + " " + msg) } } trait ShortLogger4 extends Logger4 { abstract override def log(msg: String) { super.log( if (msg.length <= 15) msg else s"${msg.substring(0, 12)}...") } } trait ConsoleLogger4 extends Logger4 { override def log(msg: String) { println(msg) } } class Account4 { protected var balance = 0.0 } abstract class SavingsAccount4 extends Account4 with Logger4 { def withdraw(amount: Double) { if (amount > balance) log("Insufficient funds") else balance -= amount } // More methods ... } object Main4 extends App { val acct1 = new SavingsAccount4 with ConsoleLogger4 with TimestampLogger4 with ShortLogger4 acct1.withdraw(100) }
11.7 当做富接口使用的特质
//当作富接口使用的特质 trait Logger5 { //这是一个富特质 //抽象方法 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 ConsoleLogger5 extends Logger5 { def log(msg: String) { println(msg) } } class Account5 { protected var balance = 0.0 } abstract class SavingsAccount5 extends Account5 with Logger5 { def withdraw(amount: Double) { if (amount > balance) severe("Insufficient funds") else balance -= amount } // More methods ... } object Main5 extends App { val acct = new SavingsAccount5 with ConsoleLogger5 acct.withdraw(100) //SEVERE: Insufficient funds }
11.8 特质中的具体字段
trait Logger6 { def log(msg: String) } trait ConsoleLogger6 extends Logger6 { def log(msg: String) { println(msg) } } trait ShortLogger6 extends Logger6 { val maxLength = 15 abstract override def log(msg: String) { super.log( if (msg.length <= maxLength) msg else s"${msg.substring(0, maxLength - 3)}...") } } class Account6 { protected var balance = 0.0 } // 只要有一个具体方法即可 class SavingsAccount6 extends Account6 with ConsoleLogger6 with ShortLogger6 { var interest = 0.0 def withdraw(amount: Double) { if (amount > balance) log("Insufficient funds") else balance -= amount } // More methods ... } object Main6 extends App { val acct = new SavingsAccount6 acct.withdraw(100) //Insufficient... println(acct.maxLength) //15 }
11.9 特质中的抽象字段
特质中未被初始化的字段在具体的子类中必须被重写。
trait Logger { def log(msg: String) } trait ConsoleLogger extends Logger { def log(msg: String) { println(msg) } } trait ShortLogger extends Logger { val maxLength: Int // An abstract field abstract override def log(msg: String) { super.log( if (msg.length <= maxLength) msg else s"${msg.substring(0, maxLength - 3)}...") } } class Account { protected var balance = 0.0 } abstract class SavingsAccount extends Account with Logger { var interest = 0.0 def withdraw(amount: Double) { if (amount > balance) log("Insufficient funds") else balance -= amount } // More methods ... } object Main extends App { val acct = new SavingsAccount with ConsoleLogger with ShortLogger { val maxLength = 20 // 在这里实现 } acct.withdraw(100) // Log message is not truncated because maxLength is 20 }
11.10 特质构造顺序
trait Logger7 { println("我在Logger7特质构造器中,嘿嘿嘿。。。") def log(msg: String) } trait ConsoleLogger7 extends Logger7 { println("我在ConsoleLogger7特质构造器中,嘿嘿嘿。。。") def log(msg: String) { println(msg) } } trait ShortLogger7 extends Logger7 { val maxLength: Int println("我在ShortLogger7特质构造器中,嘿嘿嘿。。。") abstract override def log(msg: String) { super.log(if (msg.length <= maxLength) msg else s"${msg.substring(0, maxLength - 3)}...") } } class Account7 { println("我在Account7构造器中,嘿嘿嘿。。。") protected var balance = 0.0 } abstract class SavingsAccount7 extends Account6 with ConsoleLogger7 with ShortLogger7{ println("我再SavingsAccount7构造器中") var interest = 0.0 override val maxLength: Int = 20 def withdraw(amount: Double) { if (amount > balance) log("余额不足") else balance -= amount } } object Main7 extends App { val acct = new SavingsAccount7 with ConsoleLogger7 with ShortLogger7 acct.withdraw(100) println(acct.maxLength) }
结果:
我在Account7构造器中,嘿嘿嘿。。。
我在Logger7特质构造器中,嘿嘿嘿。。。
我在ConsoleLogger7特质构造器中,嘿嘿嘿。。。
我在ShortLogger7特质构造器中,嘿嘿嘿。。。
我再SavingsAccount7构造器中
余额不足
20
执行顺序:
1、当前 new 的类的父类 Account7 的构造器执行 2、Logger7 的构造器执行 3、ConsoleLogger7 构造器执行 4、ShortLogger7 构造器执行 5、SavingsAccount7 构造器执行 6、打印
步骤总结:
1、调用当前类的超类构造器
2、第一个特质的父特质构造器
3、第一个特质构造器
4、第二个特质构造器的父特质构造器由于已经执行完成,所以不再执行
5、第二个特质构造器
6、当前类构造器
11.11 初始化特质中的字段
特质不能有构造器参数,每个特质都有一个无参数的构造器。
缺少构造器参数是特质与类之间唯一的技术差别。除此之外,特质可以具备类的所有特性,
比如具体的和抽象的字段,以及超类。现在有如下情景:
我们想通过特质来实现日志数据的输出,输出到某一个文件中
import java.io.PrintStream trait Logger8{ def log(msg:String) } trait FileLogger8 extends Logger8{ val fileName: String val out = new PrintStream(fileName) override def log(msg: String): Unit = { out.print(msg) out.flush() } } class SavingsAccount8{ } object Main8 extends App { val acct = new SavingsAccount8 with FileLogger8 { override val fileName = "2019-7-4.log" //空指针异常 } }
如果想修复如上错误,可以:
(1)使用“提前定义”
方式一:
import java.io.PrintStream trait Logger8{ def log(msg:String) } trait FileLogger8 extends Logger8{ val fileName: String val out = new PrintStream(fileName) override def log(msg: String): Unit = { out.print(msg) out.flush() } } //提前定义一: class SavingsAccount8 extends { override val fileName = "2017-11-24.log" } with FileLogger8 object Main8 extends App { val acct = new SavingsAccount8 with FileLogger8 acct.log("嘿嘿嘿") }
方式二:
package unit11 import java.io.PrintStream trait Logger8{ def log(msg:String) } trait FileLogger8 extends Logger8{ val fileName: String val out = new PrintStream(fileName) override def log(msg: String): Unit = { out.print(msg) out.flush() } } class SavingsAccount8{ } object Main8 extends App {//提前定义二: val acct1 = new { override val fileName = "hello" } with SavingsAccount8 with FileLogger8 acct1.log("nihao") }
(2)使用lazy
import java.io.PrintStream trait Logger7 { def log(msg: String) } trait FileLogger7 extends Logger7 { val fileName: String lazy val out = new PrintStream(fileName) override def log(msg: String): Unit = { out.print(msg) out.flush() } } class SavingsAccount7 { } object Main7 extends App { val acct = new SavingsAccount7 with FileLogger7 { override val fileName = "2017-11-24.log" } acct.log("哈哈哈") }
11.12 扩展类的特质
总结:
1、特质可以继承自类,以用来拓展该类的一些功能
2、所有混入该特质的类,会自动成为那个特质所继承的超类的子类
3、如果混入该特质的类,已经继承了另一个类,不就矛盾了?注意,只要继承的那个类是特质超类的子类即可。
例如:
(1)特质可以继承自类,以用来拓展该类的一些功能
trait LoggedException extends Exception{
def log(): Unit ={
println(getMessage())
} }
(2)所有混入该特质的类,会自动成为那个特质所继承的超类的子类
class UnhappyException extends LoggedException{ override def getMessage = "哦,我的上帝,我要踢爆他的屁股!" }
(3) 如果混入该特质的类,已经继承了另一个类,不就矛盾了?注意,只要继承的那个类是特质超类的子类即可。
正确:
class UnhappyException2 extends IndexOutOfBoundsException with LoggedException{ override def getMessage = "哦,我的上帝,我要踢爆他的屁股!" }
错误:
class UnhappyException3 extends JFrame with LoggedException{ override def getMessage = "哦,我的上帝,我要踢爆他的屁股!" }
11.13 自身类型
主要是为了解决特质的循环依赖问题,同时可以确保特质在不扩展某个类的情况下,依然可以做到限制混入该特质的类的类型。
比如:
//自身类型特质 trait Logger9{ this: Exception => def log(): Unit ={ println(getMessage) } }
这样一来,在该特质中,可以随意调用“自身类型”中的各种方法。
请你一定不要停下来 成为你想成为的人
感谢您的阅读,我是LXL