透明特質和類別
特質用於兩個角色
- 作為其他類別和特質的混合
- 作為值、定義或參數的類型
有些特質主要用於第一個角色,我們通常不希望在推論類型中看到它們。一個範例是 Product
特質,編譯器會將其作為混合特質新增到每個案例類別或案例物件。在 Scala 2 中,這個父特質有時會讓推論類型變得比它們應該的更複雜。範例
trait Kind
case object Var extends Kind
case object Val extends Kind
val x = Set(if condition then Val else Var)
在此,x
的推論類型為 Set[Kind & Product & Serializable]
,而預期應為 Set[Kind]
。推論此特定類型的理由如下
- 上述條件式類型為 聯合類型
Val | Var
。此聯合類型被視為「軟類型」,表示並未明確寫在原始程式中,而是來自形成某些選項類型的上界。 - 在類型推論中,軟聯合類型會擴充為類別或特質類型的最小乘積,而此乘積為聯合類型的超類型。在此範例中,此類型為
Kind & Product & Serializable
,因為這三個特質都是Val
和Var
的超特質。因此,該類型成為集合的推論元素類型。
Scala 3 允許將特質或類別標記為 transparent
,表示可在類型推論中將其隱藏。以下是遵循上述程式碼的範例,但現在使用新的透明特質 S
取代 Product
transparent trait S
trait Kind
object Var extends Kind, S
object Val extends Kind, S
val x = Set(if condition then Val else Var)
現在 x
的推論類型為 Set[Kind]
。共用透明特質 S
并未出現在推論類型中。
在先前的範例中,也可以將 Kind
宣告為 transparent
transparent trait Kind
if condition then Val else Var
的擴充聯合類型將僅包含透明特質 Kind
和 S
。在此情況下,擴充並未執行,因此 x
的類型為 Set[Val | Var]
。
根類別和特質 Any
、AnyVal
、Object
和 Matchable
被視為透明。這表示此類表達式
if condition then 1 else "hello"
將具有類型 Int | String
,而非擴充類型 Any
。
哪些特質和類別是透明的?
透過新增修飾詞 transparent
來宣告特質和類別為透明。Scala 2 特質和類別也可以透過新增 @transparentTrait
注解來宣告為透明。此注解定義在 scala.annotation
中。一旦不再需要 Scala 2/3 互通性,此注解將會被棄用並逐步淘汰。
以下類別和特質會自動視為透明
scala.Any
scala.AnyVal
scala.Matchable
scala.Product
java.lang.Object
java.lang.Comparable
java.io.Serializable
通常,除了根類別之外的透明類型是會影響繼承類別和特質的實作,而這些類別和特質通常不會自己用作類型。標準收集函式庫中的兩個範例為
IterableOps
,它為Iterable
提供方法實作。StrictOptimizedSeqOps
,它針對具備有效率索引的序列最佳化其中一些實作。
一般而言,任何遞迴延伸的特質都是宣告為透明的良好候選者。
推論規則
透明特質和類別可以像往常一樣提供為明確類型。但當推論類型時,它們通常會被省略。大致來說,類型推論的規則暗示下列內容。
- 透明特質會盡可能從交集中移除。
- 如果擴充會導致只有透明的超類型,則不會擴充聯合類型。
精確的規則如下
-
在推論類型變數的類型、val 的類型或 def 的傳回類型時,
-
其中該類型不是高階類型,
-
且其中
B
是其已知的上限,或如果不存在,則為Any
-
如果到目前為止推論的類型為
T1 & ... & Tn
形式,其中n >= 1
,請將最大數目的透明特質Ti
s 替換為Any
,同時確保產生的類型仍為上限B
的子類型。 -
不過,如果所有類型
Ti
都可以用這種方式替換,則不要執行此擴充。此條款確保不會將單一透明特質實例,例如Product
,擴充為Any
。只有當透明特質實例與其他類型結合出現時,才會將其移除。 -
如果原始類型是在前一步驟中擴充為僅包含透明特質和類別的乘積的聯合類型,請保留原始聯合類型,而不是其擴充形式。