对象是一个恰好有一个实例的类。当它被引用时,它会像一个惰性 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"
由于导入语句 import logging.Logger.info
,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
,该工厂从 String 创建一个 Email
实例。如果出现解析错误,我们会将其作为 Option[Email]
返回。
注意:如果一个类或对象有伴随对象,则两者必须在同一个文件中定义。要在 REPL 中定义伴随对象,可以在同一行定义它们,或进入 :paste
模式。
Java 程序员须知
Java 中的 static
成员在 Scala 中建模为伴随对象的普通成员。
从 Java 代码使用伴随对象时,这些成员将在具有 static
修饰符的伴随类中定义。这称为静态转发。即使您自己没有定义伴随类,也会发生这种情况。
更多资源
- 在 Scala Book 中了解有关伴随对象的更多信息