Scala 游览

单例对象

语言

对象是一个恰好有一个实例的类。当它被引用时,它会像一个惰性 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.infoinfo 方法可见。

导入需要到导入符号的“稳定路径”,而对象是一个稳定路径。

注意:如果一个 object 不是顶级对象而是嵌套在另一个类或对象中,那么该对象就像任何其他成员一样“依赖于路径”。这意味着给定两种饮料,class Milkclass OrangeJuice,类成员 object NutritionInfo “依赖于”封闭实例,即牛奶或橙汁。 milk.NutritionInfooj.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 中了解有关伴随对象的更多信息

本页贡献者