Scala 提供兩個重要的功能,用於內容抽象
- 內容參數讓您指定參數,在呼叫位置,程式設計人員可以省略這些參數,而應由內容自動提供。
- 給定實例(在 Scala 3 中)或隱式定義(在 Scala 2 中)是 Scala 編譯器可以用來填入遺失參數的術語。
內容參數
在設計系統時,通常需要將背景資訊(例如組態或設定)提供給系統的不同元件。達成此目的的一種常見方式是將組態傳遞為方法的額外參數。
在以下範例中,我們定義一個案例類別 Config
來建模一些網站組態,並在不同的方法中傳遞它。
case class Config(port: Int, baseUrl: String)
def renderWebsite(path: String, config: Config): String =
"<html>" + renderWidget(List("cart"), config) + "</html>"
def renderWidget(items: List[String], config: Config): String = ???
val config = Config(8080, "docs.scala-lang.org")
renderWebsite("/home", config)
讓我們假設組態不會在我們大部分的程式碼庫中變更。將 config
傳遞給每個方法呼叫(例如 renderWidget
)會變得非常繁瑣,也會讓我們的程式更難閱讀,因為我們需要忽略 config
參數。
將參數標記為背景
我們可以將方法的一些參數標記為背景。
def renderWebsite(path: String)(implicit config: Config): String =
"<html>" + renderWidget(List("cart")) + "</html>"
// ^
// no argument config required anymore
def renderWidget(items: List[String])(implicit config: Config): String = ???
def renderWebsite(path: String)(using config: Config): String =
"<html>" + renderWidget(List("cart")) + "</html>"
// ^
// no argument config required anymore
def renderWidget(items: List[String])(using config: Config): String = ???
透過在 Scala 3 中使用關鍵字 using
或在 Scala 2 中使用 implicit
來開始參數區段,我們告訴編譯器,它應該在呼叫位置自動尋找具有正確類型的參數。因此,Scala 編譯器會執行術語推論。
在我們呼叫 renderWidget(List("cart"))
時,Scala 編譯器會看到範圍內有一個類型為 Config
的術語(config
),並自動將它提供給 renderWidget
。因此,程式等同於上述程式。
事實上,由於我們不再需要在 renderWebsite
的實作中參照 config
,我們甚至可以在 Scala 3 的簽章中省略它的名稱
// no need to come up with a parameter name
// vvvvvvvvvvvvv
def renderWebsite(path: String)(using Config): String =
"<html>" + renderWidget(List("cart")) + "</html>"
在 Scala 2 中,隱式參數的名稱仍然是強制性的。
明確提供情境參數
我們已經了解如何對情境參數進行抽象化,並且 Scala 編譯器可以自動為我們提供參數。但是,我們如何指定在呼叫 renderWebsite
時要使用的組態?
我們明確提供參數值,就像它是一個常規參數一樣
renderWebsite("/home")(config)
就像我們使用 using
指定參數區段一樣,我們也可以使用 using
明確提供情境參數
renderWebsite("/home")(using config)
如果範圍內有多個不同的值有意義,並且我們希望確保將正確的值傳遞給函數,則明確提供情境參數會很有用。
對於所有其他情況,正如我們將在下一節中看到的那樣,還有一種將情境值帶入範圍的方法。
給定實例(Scala 2 中的隱式定義)
我們已經了解到,我們可以明確傳遞參數作為情境參數。但是,如果特定類型有一個單一的正規值,則有另一種首選方式可以讓 Scala 編譯器可以使用它:在 Scala 3 中將其標記為 given
或在 Scala 2 中將其標記為 implicit
。
implicit val config: Config = Config(8080, "docs.scala-lang.org")
// ^^^^^^
// this is the value the Scala compiler will infer
// as argument to contextual parameters of type Config
val config = Config(8080, "docs.scala-lang.org")
// this is the type that we want to provide the
// canonical value for
// vvvvvv
given Config = config
// ^^^^^^
// this is the value the Scala compiler will infer
// as argument to contextual parameters of type Config
在上面的範例中,我們指定無論何時在當前範圍中省略類型為 Config
的情境參數,編譯器都應將 config
推斷為參數。
定義 Config
類型的正規值後,我們可以如下呼叫 renderWebsite
renderWebsite("/home")
// ^
// again no argument
詳細說明 Scala 在何處尋找正規值,請參閱 常見問題。