在 GitHub 上編輯此頁面

透明特質和類別

特質用於兩個角色

  1. 作為其他類別和特質的混合
  2. 作為值、定義或參數的類型

有些特質主要用於第一個角色,我們通常不希望在推論類型中看到它們。一個範例是 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,因為這三個特質都是 ValVar 的超特質。因此,該類型成為集合的推論元素類型。

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 的擴充聯合類型將包含透明特質 KindS。在此情況下,擴充並未執行,因此 x 的類型為 Set[Val | Var]

根類別和特質 AnyAnyValObjectMatchable 被視為透明。這表示此類表達式

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

通常,除了根類別之外的透明類型是會影響繼承類別和特質的實作,而這些類別和特質通常不會自己用作類型。標準收集函式庫中的兩個範例為

一般而言,任何遞迴延伸的特質都是宣告為透明的良好候選者。

推論規則

透明特質和類別可以像往常一樣提供為明確類型。但當推論類型時,它們通常會被省略。大致來說,類型推論的規則暗示下列內容。

  • 透明特質會盡可能從交集中移除。
  • 如果擴充會導致只有透明的超類型,則不會擴充聯合類型。

精確的規則如下

  • 在推論類型變數的類型、val 的類型或 def 的傳回類型時,

  • 其中該類型不是高階類型,

  • 且其中 B 是其已知的上限,或如果不存在,則為 Any

  • 如果到目前為止推論的類型為 T1 & ... & Tn 形式,其中 n >= 1,請將最大數目的透明特質 Tis 替換為 Any,同時確保產生的類型仍為上限 B 的子類型。

  • 不過,如果所有類型 Ti 都可以用這種方式替換,則不要執行此擴充。此條款確保不會將單一透明特質實例,例如 Product,擴充為 Any。只有當透明特質實例與其他類型結合出現時,才會將其移除。

  • 如果原始類型是在前一步驟中擴充為僅包含透明特質和類別的乘積的聯合類型,請保留原始聯合類型,而不是其擴充形式。