在 GitHub 上編輯此頁面

開放類別

類別上的 open 修飾詞表示類別已規劃為擴充功能。範例

// File Writer.scala
package p

open class Writer[T]:

  /** Sends to stdout, can be overridden */
  def send(x: T) = println(x)

  /** Sends all arguments using `send` */
  def sendAll(xs: T*) = xs.foreach(send)
end Writer

// File EncryptedWriter.scala
package p

class EncryptedWriter[T: Encryptable] extends Writer[T]:
  override def send(x: T) = super.send(encrypt(x))

開放類別通常會附帶一些文件,說明類別方法之間的內部呼叫模式,以及可以覆寫的掛勾。我們稱之為類別的擴充合約。它與類別與其使用者之間的外部合約不同。

未開放的類別仍可以擴充,但前提是必須符合兩個替代條件之一

  • 擴充類別與被擴充類別位於同一個原始碼檔案中。在此情況下,擴充通常是內部實作事項。

  • 語言功能 adhocExtensions 已啟用擴充類別。這通常是由擴充原始碼檔案中的匯入子句啟用

    import scala.language.adhocExtensions
    

    或者,可以使用編譯器選項 -language:adhocExtensions 啟用此功能。如果未啟用此功能,編譯器將發出「功能」警告。例如,如果捨棄類別 Writer 上的 open 修飾詞,編譯 EncryptedWriter 會產生警告

    -- Feature Warning: EncryptedWriter.scala:6:14 ----
      |class EncryptedWriter[T: Encryptable] extends Writer[T]
      |                                              ^
      |Unless class Writer is declared 'open', its extension
      | in a separate file should be enabled
      |by adding the import clause 'import scala.language.adhocExtensions'
      |or by setting the compiler option -language:adhocExtensions.
    

動機

在撰寫類別時,對於可擴充性有三個可能的預期

  1. 此類別應允許擴充。這表示應該預期類別有經過仔細規劃和文件化的擴充合約。

  2. 禁止擴充類別,例如為了提供正確性或安全性保證。

  3. 兩者之間沒有明確的決定。此類別並非先驗地打算擴充,但如果其他人發現以臨時基礎擴充很有用,就讓他們繼續進行。不過,在這種情況下,他們必須自行負責。沒有文件化的擴充合約,而且類別的未來版本可能會中斷擴充(例如,透過重新排列內部呼叫模式)。

這三種情況的區分很明確,對 (1) 使用 open,對 (2) 使用 final,對 (3) 不使用修飾詞。

最好避免在程式碼庫中進行臨時擴充,因為它們往往會導致脆弱且難以演化的系統。但仍有一些情況下,這些擴充很有用:例如,在測試中模擬類別,或套用臨時修補程式以新增功能或修正函式庫類別中的錯誤。這就是為什麼允許臨時擴充,但前提是透過語言功能匯入有明確的選擇加入。

詳細資料

  • open 是軟修飾詞。除非位於修飾詞位置,否則會視為一般識別碼。
  • open 類別不能是 finalsealed
  • 特質或 abstract 類別永遠是 open,因此對它們而言 open 是多餘的。

sealed 的關係

既不是 abstract 也不是 open 的類別類似於 sealed 類別:它仍然可以擴充,但只能在同一個來源檔案中。不同之處在於,如果在另一個來源檔案中嘗試擴充類別會發生什麼事。對於 sealed 類別,這是一個錯誤,而對於一個簡單的非開放類別,只要啟用 adhocExtensions 功能,這仍然被允許,否則會給出警告。

遷移

open 是 Scala 3 中的新修飾詞。為了在 Scala 2.13 和 Scala 3.0 之間允許交叉編譯而沒有警告,僅在 -source future 下產生臨時擴充的警告。它將在 Scala 3.4 之後 預設產生。