外貿(mào)響應(yīng)式網(wǎng)站google服務(wù)框架
在 Scala 中,特質(zhì)(Trait)是一種強(qiáng)大的工具,用于實(shí)現(xiàn)代碼的復(fù)用和組合。當(dāng)一個(gè)類混入(with
)多個(gè)特質(zhì)時(shí),可能會(huì)出現(xiàn)方法沖突的情況。為了解決這種沖突,Scala 引入了最右優(yōu)先原則(Rightmost First Rule),也稱為線性化規(guī)則(Linearization Rule)。
最右優(yōu)先原則
最右優(yōu)先原則的核心思想是:在混入多個(gè)特質(zhì)時(shí),最右邊的特質(zhì)會(huì)優(yōu)先生效。也就是說,如果一個(gè)方法在多個(gè)特質(zhì)中都有定義,那么最右邊的特質(zhì)中的方法會(huì)覆蓋左邊特質(zhì)中的方法。
示例1
trait A {def greet(): String = "Hello from A"
}trait B {def greet(): String = "Hello from B"
}class C extends A with B {override def greet(): String = super.greet()
}val obj = new C
println(obj.greet()) // 輸出: Hello from B
在上面的例子中:
-
類?
C
?混入了特質(zhì)?A
?和?B
。 -
根據(jù)最右優(yōu)先原則,
B
?中的?greet
?方法會(huì)覆蓋?A
?中的?greet
?方法。 -
因此,調(diào)用?
obj.greet()
?時(shí),輸出的是?B
?中的實(shí)現(xiàn)。
線性化規(guī)則
最右優(yōu)先原則是 Scala 線性化規(guī)則的一部分。Scala 會(huì)為每個(gè)類生成一個(gè)線性化順序(Linearization Order),這個(gè)順序決定了方法調(diào)用的優(yōu)先級(jí)。
線性化順序的生成規(guī)則
-
類的線性化順序從最具體的類開始,逐步向更通用的類擴(kuò)展。
-
混入的特質(zhì)按照從右到左的順序排列。
-
每個(gè)特質(zhì)只會(huì)在線性化順序中出現(xiàn)一次。
示例2
trait A {def greet(): String = "Hello from A"
}trait B extends A {override def greet(): String = "Hello from B"
}trait C extends A {override def greet(): String = "Hello from C"
}class D extends B with C {override def greet(): String = super.greet()
}val obj = new D
println(obj.greet()) // 輸出: Hello from C
在這個(gè)例子中:
-
類?
D
?的線性化順序是:D -> C -> B -> A
。 -
根據(jù)最右優(yōu)先原則,
C
?中的?greet
?方法會(huì)覆蓋?B
?中的?greet
?方法。 -
因此,調(diào)用?
obj.greet()
?時(shí),輸出的是?C
?中的實(shí)現(xiàn)。
super
?的調(diào)用
在特質(zhì)中,super
?的調(diào)用是動(dòng)態(tài)綁定的,它會(huì)根據(jù)線性化順序調(diào)用下一個(gè)特質(zhì)或類中的方法。
示例3
trait A {def greet(): String = "Hello from A"
}trait B extends A {override def greet(): String = s"${super.greet()} and Hello from B"
}trait C extends A {override def greet(): String = s"${super.greet()} and Hello from C"
}class D extends B with C {override def greet(): String = super.greet()
}val obj = new D
println(obj.greet()) // 輸出: Hello from A and Hello from B and Hello from C
如果你還是有疑問,接下來,是更加具體的分析:
?
在示例3中,輸出的是Hello from A and Hello from B and Hello from C
,而不是?Hello from A and Hello from C and Hello from B
。這看起來似乎與最右優(yōu)先原則相矛盾,但實(shí)際上這是由 Scala 的線性化規(guī)則(Linearization Rule)決定的。
線性化規(guī)則詳解
Scala 的線性化規(guī)則決定了方法調(diào)用的順序。具體來說,當(dāng)一個(gè)類混入多個(gè)特質(zhì)時(shí),Scala 會(huì)生成一個(gè)線性化順序,這個(gè)順序決定了?super
?調(diào)用的行為。
線性化順序的生成規(guī)則
-
從最具體的類開始,逐步向更通用的類擴(kuò)展。
-
混入的特質(zhì)按照從右到左的順序排列。
-
每個(gè)特質(zhì)只會(huì)在線性化順序中出現(xiàn)一次。
在示例3中:
class D extends B with C
-
D
?的線性化順序是:D -> C -> B -> A
。
線性化順序的解釋
-
D
:最具體的類。 -
C
:因?yàn)?C
?是最右邊的特質(zhì),所以它排在?B
?前面。 -
B
:B
?是左邊的特質(zhì),排在?C
?后面。 -
A
:A
?是?B
?和?C
?的共同父特質(zhì),排在最后。
因此,D
?的線性化順序是:D -> C -> B -> A
。
super
?的調(diào)用行為
在 Scala 中,super
?的調(diào)用是動(dòng)態(tài)綁定的,它會(huì)根據(jù)線性化順序調(diào)用下一個(gè)特質(zhì)或類中的方法。
例子分析
trait A {def greet(): String = "Hello from A"
}trait B extends A {override def greet(): String = s"${super.greet()} and Hello from B"
}trait C extends A {override def greet(): String = s"${super.greet()} and Hello from C"
}class D extends B with C {override def greet(): String = super.greet()
}val obj = new D
println(obj.greet()) // 輸出: Hello from A and Hello from B and Hello from C
-
D
?中的?greet
?方法:-
調(diào)用?
super.greet()
,根據(jù)線性化順序,super
?指向?C
。
-
-
C
?中的?greet
?方法:-
調(diào)用?
super.greet()
,根據(jù)線性化順序,super
?指向?B
。
-
-
B
?中的?greet
?方法:-
調(diào)用?
super.greet()
,根據(jù)線性化順序,super
?指向?A
。
-
-
A
?中的?greet
?方法:-
返回?
"Hello from A"
。
-
-
方法調(diào)用的堆棧:
-
A
?返回?"Hello from A"
。 -
B
?在其基礎(chǔ)上追加?" and Hello from B"
,得到?"Hello from A and Hello from B"
。 -
C
?在其基礎(chǔ)上追加?" and Hello from C"
,得到?"Hello from A and Hello from B and Hello from C"
。
-
為什么不是?Hello from A and Hello from C and Hello from B
?
-
因?yàn)?
super
?的調(diào)用是根據(jù)線性化順序動(dòng)態(tài)綁定的,而不是簡單地按照最右優(yōu)先原則直接覆蓋。 -
線性化順序是?
D -> C -> B -> A
,所以?C
?的?super
?指向?B
,B
?的?super
?指向?A
。 -
因此,
C
?的?greet
?方法會(huì)先調(diào)用?B
?的?greet
?方法,而?B
?的?greet
?方法會(huì)調(diào)用?A
?的?greet
?方法。
總結(jié)
-
最右優(yōu)先原則:決定了特質(zhì)的優(yōu)先級(jí),最右邊的特質(zhì)會(huì)優(yōu)先生效。
-
線性化規(guī)則:決定了?
super
?的調(diào)用順序,super
?會(huì)根據(jù)線性化順序動(dòng)態(tài)綁定到下一個(gè)特質(zhì)或類。 -
在示例3中,線性化順序是?
D -> C -> B -> A
,因此輸出的順序是?Hello from A and Hello from B and Hello from C
。
在示例2中,為什么輸出是?Hello from C
,而不是?Hello from A and Hello from C?
代碼分析
trait A {def greet(): String = "Hello from A"
}trait B extends A {override def greet(): String = "Hello from B"
}trait C extends A {override def greet(): String = "Hello from C"
}class D extends B with C {override def greet(): String = super.greet()
}val obj = new D
println(obj.greet()) // 輸出: Hello from C
-
特質(zhì)的繼承關(guān)系:
-
B
?和?C
?都繼承自?A
,并且都重寫了?greet
?方法。 -
D
?混入了?B
?和?C
,并且重寫了?greet
?方法,調(diào)用了?super.greet()
。
-
-
線性化順序:
-
當(dāng)?
D
?混入?B
?和?C
?時(shí),Scala 會(huì)生成一個(gè)線性化順序。線性化順序的規(guī)則是:-
從最具體的類開始,逐步向更通用的類擴(kuò)展。
-
混入的特質(zhì)按照從右到左的順序排列。
-
每個(gè)特質(zhì)只會(huì)在線性化順序中出現(xiàn)一次。
-
-
對于?
class D extends B with C
,線性化順序是:D -> C -> B -> A
。
-
-
super
?的調(diào)用行為:-
在?
D
?的?greet
?方法中,super.greet()
?會(huì)根據(jù)線性化順序調(diào)用下一個(gè)特質(zhì)或類中的?greet
?方法。 -
線性化順序是?
D -> C -> B -> A
,因此?super.greet()
?會(huì)調(diào)用?C
?中的?greet
?方法。
-
-
C
?中的?greet
?方法:-
C
?中的?greet
?方法直接返回?"Hello from C"
,沒有調(diào)用?super.greet()
。 -
因此,
C
?的?greet
?方法不會(huì)繼續(xù)調(diào)用?B
?或?A
?的?greet
?方法。
-
為什么輸出是?Hello from C
?
-
在?
D
?的?greet
?方法中,super.greet()
?調(diào)用的是?C
?的?greet
?方法。 -
C
?的?greet
?方法直接返回?"Hello from C"
,沒有繼續(xù)調(diào)用?super.greet()
(即沒有調(diào)用?B
?或?A
?的?greet
?方法)。 -
因此,最終的輸出是?
"Hello from C"
。
為什么不是?Hello from A and Hello from C
?
-
如果希望輸出?
Hello from A and Hello from C
,需要在?C
?的?greet
?方法中顯式調(diào)用?super.greet()
,將?A
?的行為與?C
?的行為組合起來。 -
例如:
trait C extends A {override def greet(): String = s"${super.greet()} and Hello from C"
}
修改后,C
?的?greet
?方法會(huì)先調(diào)用?A
?的?greet
?方法,然后追加?" and Hello from C"
。此時(shí),輸出會(huì)是?Hello from A and Hello from C
。
修改后的代碼
trait A {def greet(): String = "Hello from A"
}trait B extends A {override def greet(): String = "Hello from B"
}trait C extends A {override def greet(): String = s"${super.greet()} and Hello from C"
}class D extends B with C {override def greet(): String = super.greet()
}val obj = new D
println(obj.greet()) // 輸出: Hello from A and Hello from C
總結(jié)
-
默認(rèn)行為:在?
C
?的?greet
?方法中,如果沒有調(diào)用?super.greet()
,則只會(huì)執(zhí)行?C
?的邏輯,輸出?Hello from C
。 -
組合行為:如果希望將父特質(zhì)的行為與當(dāng)前特質(zhì)的行為組合起來,需要在重寫方法時(shí)顯式調(diào)用?
super.greet()
。 -
線性化順序:
super
?的調(diào)用是根據(jù)線性化順序動(dòng)態(tài)綁定的,線性化順序決定了方法調(diào)用的優(yōu)先級(jí)。