類可以實現任意數量的特質
特質可以要求實現他們的類具備特定的字段、方法或超類
特質可以提供方法和字段的實現
多個特質疊加時,後面的特質其方法先被執行
特質
同時擁有抽象方法和具體方法,以及狀態
類可以實現多個特質
特質中未被實現的方法默認就是抽象的
繼承特質使用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) ;
}