物件是一個僅有一個實例的類別。它在被參照時會延遲建立,就像延遲 val 一樣。
作為頂層值,物件是單例。
作為封閉類別的成員或作為局部值,它的行為完全就像延遲 val。
定義單例物件
物件是一個值。物件的定義看起來像一個類別,但使用關鍵字 object
object Box
以下是具有方法的物件範例
package logging
object Logger {
def info(message: String): Unit = println(s"INFO: $message")
}
package logging
object Logger:
def info(message: String): Unit = println(s"INFO: $message")
方法 info
可以從程式中的任何地方匯入。建立像這樣的實用程式方法是單例物件的常見使用案例。
讓我們看看如何在另一個套件中使用 info
import logging.Logger.info
class Project(name: String, daysToComplete: Int)
class Test {
val project1 = new Project("TPS Reports", 1)
val project2 = new Project("Website redesign", 5)
info("Created projects") // Prints "INFO: Created projects"
}
import logging.Logger.info
class Project(name: String, daysToComplete: Int)
class Test:
val project1 = Project("TPS Reports", 1)
val project2 = Project("Website redesign", 5)
info("Created projects") // Prints "INFO: Created projects"
info
方法會因為匯入陳述式 import logging.Logger.info
而可見。
匯入需要一個到匯入符號的「穩定路徑」,而物件是一個穩定路徑。
注意:如果 object
不是頂層,而是巢狀在另一個類別或物件中,則物件會像任何其他成員一樣「路徑依賴」。這表示給定兩種飲料,class Milk
和 class OrangeJuice
,類別成員 object NutritionInfo
「依賴」於封閉實例,可能是牛奶或柳橙汁。 milk.NutritionInfo
與 oj.NutritionInfo
完全不同。
伴隨物件
與類別同名的物件稱為伴隨物件。相反地,類別是物件的伴隨類別。伴隨類別或物件可以存取其伴隨的私有成員。對伴隨類別的實例不特定的方法和值,請使用伴隨物件。
import scala.math.{Pi, pow}
case class Circle(radius: Double) {
import Circle._
def area: Double = calculateArea(radius)
}
object Circle {
private def calculateArea(radius: Double): Double = Pi * pow(radius, 2.0)
}
val circle1 = Circle(5.0)
circle1.area
import scala.math.{Pi, pow}
case class Circle(radius: Double):
import Circle.*
def area: Double = calculateArea(radius)
object Circle:
private def calculateArea(radius: Double): Double = Pi * pow(radius, 2.0)
val circle1 = Circle(5.0)
circle1.area
class Circle
有成員 area
,這是特定於每個實體,而單例 object Circle
有方法 calculateArea
,每個實體都可以使用。
伴生物件也可以包含工廠方法
class Email(val username: String, val domainName: String)
object Email {
def fromString(emailString: String): Option[Email] = {
emailString.split('@') match {
case Array(a, b) => Some(new Email(a, b))
case _ => None
}
}
}
val scalaCenterEmail = Email.fromString("[email protected]")
scalaCenterEmail match {
case Some(email) => println(
s"""Registered an email
|Username: ${email.username}
|Domain name: ${email.domainName}
""".stripMargin)
case None => println("Error: could not parse email")
}
class Email(val username: String, val domainName: String)
object Email:
def fromString(emailString: String): Option[Email] =
emailString.split('@') match
case Array(a, b) => Some(Email(a, b))
case _ => None
val scalaCenterEmail = Email.fromString("[email protected]")
scalaCenterEmail match
case Some(email) => println(
s"""Registered an email
|Username: ${email.username}
|Domain name: ${email.domainName}
""".stripMargin)
case None => println("Error: could not parse email")
object Email
包含一個工廠 fromString
,它會從字串建立一個 Email
實體。如果發生剖析錯誤,我們會將它傳回為 Option[Email]
。
注意:如果一個類別或物件有伴生,兩者都必須定義在同一個檔案中。要在 REPL 中定義伴生,請在同一行定義它們,或輸入 :paste
模式。
給 Java 程式設計師的注意事項
Java 中的 static
成員在 Scala 中建模為伴生物件的普通成員。
從 Java 程式碼使用伴生物件時,這些成員會定義在具有 static
修飾詞的伴生類別中。這稱為靜態轉發。即使您自己沒有定義伴生類別,也會發生這種情況。
更多資源
- 在 Scala Book 中深入了解伴生物件