任何類只能`extends`一個父類,但可以混入多個特質,混入用關鍵字`with`實現,相當于Java中用`implements`關鍵字實現接口一樣。
```scala
// 1. 在聲明類的時候混入特質
class A extedns B with TraitC with TraitD with ...
// 2. 在new對象時混入特質
val A = new A with TraitC with TraitD with ...
```
<br/>
**1. 混入特質**
```scala
// 抽象類
abstract class Animal {
val message:String = "abstract class Animal"
}
// 類
class Dog extends Animal {
override val message: String = "class Dog"
}
// 特質
trait Mammal extends Animal {
override val message: String = "trait Mammal"
}
// 特質
trait Pet extends Animal {
override val message: String = "trait Pet"
}
// 混入特質
class Twoha extends Dog with Mammal with Pet
object App {
def main(args: Array[String]): Unit = {
val twoHa = new Twoha
println(twoHa.message) // trait Pet
}
}
```
* 當混入有多個同名的成員時,調用的是最右邊的成員。如上面輸出的`trait Pet`為最右邊的`message`,當然如果子類重寫了該成員,調用的就是子類的了。
**2. 混入特質的構造順序**
構造順序由左往右,如果前面已經有某個父類被構造一次,則后面不再重新構造,比如上面的Animal類雖然被多個子類繼承,但從左到右只被構造一次。
具體構造順序從左往右如下:
```scala
超類構造器 -> 父特質構造器 -> 子特質構造器 -> 子類構造器
所以 class Twoha extends Dog with Mammal with Pet 的構造順序如下:
Animal -> Dog -> Mammal -> Pet -> Twoha
```
<br/>
**3. 特質對 `super`的動態調用**
```scala
class Root {
def hello() { println("Hello, Root!")}
}
class SubA extends Root {
override def hello(){
super.hello() // 在類中super調用為靜態調用
println("Hello, SubA!")
}
}
trait SubB extends Root {
override def hello(){
super.hello(); // 在特質中super調用是動態調用
println("Hello, SubB")
}
}
```
在 SubA 中,`super` 的調用是靜態綁定的,父類 Root 的 hello() 將被調用。而在 SubB 中,`super` 的調用是動態綁定的,即在定義特質 SubB 的時候,`super` 還不確定,直到特質被混入到具體類的時候才確定。
```scala
object HelloWorld{
def main(args: Array[String]): Unit = {
val a = new Root with SubB // SubB被混入到Root中,則super為Root類, super.hello()為Root中的hello()
a.hello()
// Hello, Root!
// Hello, SubB!
val b = new SubA with SubB // SubB被混入到SubA中,則super為SubA類, super.hello()為SubA中的hello()
// 調用是SubA中的hello()
b.hello()
// Hello, Root!
// Hello, SubA!
// Hello, SubB!
}
}
```
<br/>
**4. 特質線性化**
線性化給了在多個特質中 `super` 被解析的順序。
```scala
class Animal
trait Furry extends Animal
trait HasLegs extends Animal
trait FourLegged extends HasLegs
class Cat extends Animal with Furry with FourLegged
```
類 Cat 的繼承層級和線性化次序展示在下圖。
繼承次序使用傳統的 UML 標注指明:白色箭頭表明繼承,箭頭指向超類型。黑色箭頭說明線性化次序,箭頭指向 `super` 調用解決的方向。

Cat 的線性化次序為:Cat >> FourLegged >> HasLegs >> Furry >> Animal
`>>`意思是:串接并去掉重復項, 右側勝出,如下
```scala
class Cat extends Animal with Furry with FourLegged
lin(Cat) = Cat >> lin(FourLegged) >> lin(Furry) >> lin(Animal)
= Cat >> (FourLegged >> HasLegs) >> (Furry >> Animal) >> (Animal)
= Cat >> FourLegged >> HasLegs >> Furry >> Animal
```
<br/>
**5. 特質調用鏈**
特質調用鏈就是子特質重寫父特質的方法,然后在該方法中使用`super`關鍵字調用父特質的方法。
```scala
trait TraitA {
def print = println("traitA")
}
trait TraitB extends TraitA {
override def print: Unit = {
super.print // 使用super調用父類的print函數
println("traitB")
}
}
trait TraitC extends TraitB {
override def print: Unit = {
super.print
println("traitC")
}
}
class ClassD {}
object App {
def main(args: Array[String]): Unit = {
val classD = new ClassD with TraitC with TraitB with TraitA
classD.print
}
}
==========Output==========
traitA
traitB
traitC
```
可以看到 print 方法<mark>從右往左</mark>開始被調用,形成一個調用鏈。
總結:越靠近后面的特質越優先起作用,當調用帶混入的類的方法時,最右側特質的方法首先被調用。如果哪個方法調用了 `super`,則它調用其左側特質的方法。
- Scala是什么?
- Scala特性
- 開發環境搭建
- 環境搭建
- windows下的環境搭建
- IntelliJ IDEA環境搭建
- Scala關鍵字
- Hello, World
- 數據類型
- 數據類型
- 數據類型層次結構
- 字面量
- Null類型
- Nothing類型
- Unit類型
- 變量與常量
- type定義類型別名
- 字符串插值器
- 條件控制
- 循環控制
- 數組
- 元組
- 集合
- 集合分類
- List常用操作
- Set常用操作
- Map常用操作
- 函數
- 函數聲明與調用
- 函數與方法的區別
- 函數注意事項
- 匿名函數
- 可變參數
- 高階函數
- 中置表達式
- 函數嵌套
- 函數科里化
- 隱式參數
- 隱式函數
- 閉包
- 類和對象
- Java與Scala的比較
- 有關類與對象概念
- 類
- 類的定義和調用
- 類的繼承
- 抽象類
- 單例對象
- 伴生對象和伴生類
- 特質
- 定義特質
- 混入特質
- 抽象類與特質的選擇
- 自身類型
- 依賴注入
- this別名
- 樣例類
- 枚舉類
- 泛型類
- 包與包對象
- 模式匹配
- 基本語法
- 匹配模式
- 偏函數
- 注解
- 運算符
- 正則表達式
- 隱式類
- 異常處理
- 高級類型
- 結構類型
- 復合類型