类可以实现任意数量的特质
特质可以要求实现他们的类具备特定的字段、方法或超类
特质可以提供方法和字段的实现
多个特质叠加时,后面的特质其方法先被执行
特质
同时拥有抽象方法和具体方法,以及状态
类可以实现多个特质
特质中未被实现的方法默认就是抽象的
继承特质使用extends而不是implements
多个特质使用with
所有的Java接口都可以作为Scala特质使用
extends后面的Logger with Cloneable with Serializable 可以看做一个整体,然后再由类扩展
trait Logger {
def log ( msg: String)
}
class ConsoleLogger extends Logger with Cloneable with Serializable {
override def log ( msg: String) : Unit = {
println ( msg)
}
}
带有具体实现的特质
trait ConsoleLogger {
def log ( msg: String) : Unit = {
println ( msg)
}
}
class SavingsAccount extends Account with ConsoleLogger{
def withdraw ( amount: Double) : Unit = {
if ( amount > balance) log ( "Insufficient funds" )
else balance -= amount
}
}
class Account {
val id = Account. newUniqueNumber ( )
protected var balance = 0.0
def deposit ( amount: Double) : Unit = {
balance += amount
}
def CXYE= {
println ( "余额:" + balance)
}
}
object Account{
private var lastNumber = 0
private def newUniqueNumber ( ) : Int= {
lastNumber+= 1
lastNumber
}
}
带有特质的对象
构造对象时可以添加特质
一个抽象类不能被实例化,不管是否包含抽象字段或方法。如果抽象类的所有字段方法都是具体的,并且扩展了一个特质,那么这个抽象类就能实例化为带特质的对象
abstract class A { val a= "sr" }
trait B
new A with B
trait Logger {
def log ( msg: String)
}
trait ConsoleLogger extends Logger {
def log ( msg: String) : Unit = {
println ( msg)
}
}
abstract class SavingsAccount extends Account with Logger{
def withdraw ( amount: Double) : Unit = {
if ( amount > balance) log ( "Insufficient funds" )
else balance -= amount
}
}
class Account {
val id = Account. newUniqueNumber ( )
protected var balance = 0.0
def deposit ( amount: Double) : Unit = {
balance += amount
}
def CXYE= {
println ( "余额:" + balance)
}
}
object Account{
private var lastNumber = 0
private def newUniqueNumber ( ) : Int= {
lastNumber+= 1
lastNumber
}
}
叠加在一起的特质
最后出现的方法先被调用,其次往前推进,最前面的方法最后被调用
对于acc1,ShortLogger的log方法先被执行,“Insufficient funds"被截断后变为了"Insufficient”,然后super.log调用TimestampLogger的log方法将"Insufficient"加上时间戳,最后是ConsoleLogger的log方法将加上时间戳后的字符串打印到屏幕
对于acc2,TimestampLogger的log方法先被执行,然后super.log调用的是ShortLogger
trait Logger {
def log ( msg: String)
}
trait ConsoleLogger extends Logger {
def log ( msg: String) : Unit = {
println ( msg)
}
}
trait TimestampLogger extends ConsoleLogger {
println ( "LongLogger" )
override def log ( msg: String) : Unit = super . log ( s"${java.time.Instant.now()} ${msg}" )
}
trait ShortLogger extends ConsoleLogger {
println ( "ShortLogger" )
override def log ( msg: String) : Unit = super . log ( if ( msg. length<= 15 ) msg else s"${msg.substring(0,12)}" )
}
abstract class SavingsAccount extends Account with Logger{
def withdraw ( amount: Double) : Unit = {
if ( amount > balance) log ( "Insufficient funds" )
else balance -= amount
}
}
val acc1 = new SavingsAccount with TimestampLogger with ShortLogger
acc1. withdraw ( 1000 )
val acc2 = new SavingsAccount with ShortLogger with TimestampLogger
acc2. withdraw ( 1000 )
控制具体哪个特质的方法被调用,使用super[特质].log(…),给出的类型必须是直接超类,不能是更远的特质或类
例如,在ShortLogger中调用Super[ConsoleLogger].log,会直接调用ConsoleLogger的log方法,而不是先TimestampLogger再ConsoleLogger
super [ ConsoleLogger] . log ( if ( msg. length<= 15 ) msg else s"${msg.substring(0,12)}" )
特质中重写抽象方法
abstract override def log(msg:String)
当做富接口使用的特质
trait Logger {
def log ( msg: String)
def info ( msg: String) = { log ( s"INFO: ${msg}" ) }
def warn ( msg: String) = { log ( s"WARN: ${msg}" ) }
def severe ( msg: String) = { log ( s"SEVERE: ${msg}" ) }
}
abstract class SavingsAccount extends Account with Logger{
def withdraw ( amount: Double) : Unit = {
if ( amount > balance) severe ( "Insuffient funds" )
else balance -= amount
}
}
特质中的具体字段
特质中的字段可以是具体的也可以是抽象的,取决于你是否给出初始值
使用该特质的类会获得一个字段与之对应,这些字段不能被继承,只是简单的被添加到了子类中
在JVM中一个类只能扩展一个超类,因此当继承自特质的时候,特质的字段不能以相同的方式继承
maxLength书上说不能继承,这里也能继承,不知道怎么回事
class SavingsAccount extends Account with ConsoleLogger with ShortLogger {
balance = 0.01
override val maxLength= 20
println ( maxLength)
def withdraw ( amount: Double) : Unit = {
if ( amount > balance) log ( "Insuffient funds" )
else balance -= amount
}
}
trait Logger {
def log ( msg: String)
def info ( msg: String) = { log ( s"INFO: ${msg}" ) }
def warn ( msg: String) = { log ( s"WARN: ${msg}" ) }
def severe ( msg: String) = { log ( s"SEVERE: ${msg}" ) }
}
trait ConsoleLogger extends Logger {
println ( "ConsoleLogger" )
def log ( msg: String) : Unit = {
println ( msg)
}
}
trait TimestampLogger extends ConsoleLogger {
println ( "LongLogger" )
override def log ( msg: String) : Unit = super . log ( s"${java.time.Instant.now()} ${msg}" )
}
trait ShortLogger extends ConsoleLogger {
val maxLength = 15
println ( "ShortLogger" )
override def log ( msg: String) : Unit = super [ ConsoleLogger] . log ( if ( msg. length<= maxLength) msg else s"${msg.substring(0,maxLength)}" )
}
class Account {
val id = Account. newUniqueNumber ( )
protected var balance = 0.0
def deposit ( amount: Double) : Unit = {
balance += amount
}
def CXYE= {
println ( "余额:" + balance)
}
}
object Account{
private var lastNumber = 0
private def newUniqueNumber ( ) : Int= {
lastNumber+= 1
lastNumber
}
}
特质中的抽象字段
未被初始化的字段在具体子类中必须被重写,跟抽象类一样
特质的构造顺序
特质也可以有构造器,由字段的初始化和其他特质体中的语句构成
首先调用超类的构造器
特质的构造器在超类的构造器之后、类构造器之前执行
特质由左到右被构造
在每个特质中,父特质先被构造
如果一个特质共有一个父特质,而那个特质已经被构造,那么这个父特质不会被再次构造
所有特质构造完毕,子类被构造
总结起来就是,从左到右,从祖宗到子孙 的顺序。SavingAccount是最子孙的,最后被构造。
对于下面的一个类,其构造顺序如下
Account:超类
Logger: 第一个特质的父特质ConsoleLogger的父特质,爷爷特质
ConsoleLogger: 第一个特质的父特质
TimestampLogger:第一个特质
ShortLogger:第二个特质,其父特质和爷爷特质都已被构造,不再被构造了
SavingAccount:类
class SavingAccount extends Account with TimestampLogger with ShortLogger
初始化特质中的字段
特质不能有构造器参数,每个特质都有一个无参数的构造器
缺少构造器参数是特质与类之间的唯一技术差别
初始化特质的字段得使用提前定义 或懒值,
trait FileLogger extends Logger {
val filename: String
val out = new java. io. PrintStream ( filename)
def log ( msg: String) = {
out. println ( msg)
out. flush ( )
}
}
val acct = new SavingsAccount with FileLogger{
val filename = "mylog.log"
}
val acct = new { val filename = "myapp.log" } with SavingAccount with FileLogger {
log ( "hahaha" )
}
class SavingAccount extends { val filename= “mylog. log”} with Account with FileLogger{
}
扩展类的特质
类也可以扩展特质
特质的超类是任何混入该特质的类的超类
类A扩展自B和特质C,如果C有超类D,那么B是D的子类才可以,如果B和D不相关,那么就不能扩展了。
自身类型
自身类型:如果特质的定义开头是this:类型A=>
,它只能被混入指定类型A的子类,只能类型A的子类能扩展这个特质。
结构类型:给出类必须有的方法,而不是类的名称,类定义的开头是this:{def 方法F}=>
,这个特质可以被混入任何拥有方法F的类。
特质的背后发生了什么
只有抽象方法的特质会被简单地变成java接口
有具体方法的时候就变成抽象类了
trait Logger2 {
val maxLength = 15
val minLength : Int
def log ( msg: String)
def log2 ( msg: String) = println ( msg)
}
class Account2 extends Logger2 {
val minLength= 0
def log ( msg: String) = "aa"
}
public class Account implements Logger2 {
private final int minLength;
private final int maxLength;
public int maxLength ( ) ;
public void Logger2$_setter_$maxLength_$eq ( int ) ;
public void log2 ( java. lang. String) ;
public int minLength ( ) ;
public void log ( java. lang. String) ;
public Account ( ) ;
}
public abstract class Logger2 $class {
public static void log2 ( Logger2, java. lang. String) ;
public static void $init$( Logger2) ;
}